diff --git a/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-SoC.patch b/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-SoC.patch index 5bd7da4a43..618b506f97 100644 --- a/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-SoC.patch +++ b/target/linux/airoha/patches-6.12/310-09-net-pcs-airoha-add-PCS-driver-for-Airoha-SoC.patch @@ -31,7 +31,7 @@ Signed-off-by: Christian Marangi --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig -@@ -44,4 +44,6 @@ config PCS_RZN1_MIIC +@@ -51,4 +51,6 @@ config PCS_RZN1_MIIC on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in pass-through mode for MII. @@ -40,7 +40,7 @@ Signed-off-by: Christian Marangi endmenu --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile -@@ -9,3 +9,5 @@ obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o +@@ -10,3 +10,5 @@ obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o diff --git a/target/linux/generic/backport-6.12/650-v6.13-net-phylink-allow-mac_select_pcs-to-remove-a-PCS.patch b/target/linux/generic/backport-6.12/650-v6.13-net-phylink-allow-mac_select_pcs-to-remove-a-PCS.patch new file mode 100644 index 0000000000..199efc6f9c --- /dev/null +++ b/target/linux/generic/backport-6.12/650-v6.13-net-phylink-allow-mac_select_pcs-to-remove-a-PCS.patch @@ -0,0 +1,38 @@ +From 486dc391ef439d45db3f7eda2229560fd2b52a78 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 16 Oct 2024 10:58:34 +0100 +Subject: [PATCH] net: phylink: allow mac_select_pcs() to remove a PCS + +phylink has historically not permitted a PCS to be removed. An attempt +to permit this with phylink_set_pcs() resulted in comments indicating +that there was no need for this. This behaviour has been propagated +forward to the mac_select_pcs() approach as it was believed from these +comments that changing this would be NAK'd. + +However, with mac_select_pcs(), it takes more code and thus complexity +to maintain this behaviour, which can - and in this case has - resulted +in a bug. If mac_select_pcs() returns NULL for a particular interface +type, but there is already a PCS in-use, then we skip the pcs_validate() +method, but continue using the old PCS. Also, it wouldn't be expected +behaviour by implementers of mac_select_pcs(). + +Allow this by removing this old unnecessary restriction. + +Signed-off-by: Russell King (Oracle) +Reviewed-by: Vladimir Oltean +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/phylink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1375,7 +1375,7 @@ static void phylink_major_config(struct + return; + } + +- pcs_changed = pcs && pl->pcs != pcs; ++ pcs_changed = pl->pcs != pcs; + } + + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); diff --git a/target/linux/generic/backport-6.12/651-v6.14-net-phylink-add-support-for-PCS-supported_interfaces.patch b/target/linux/generic/backport-6.12/651-v6.14-net-phylink-add-support-for-PCS-supported_interfaces.patch new file mode 100644 index 0000000000..044f49b392 --- /dev/null +++ b/target/linux/generic/backport-6.12/651-v6.14-net-phylink-add-support-for-PCS-supported_interfaces.patch @@ -0,0 +1,61 @@ +From fbb9a9d263a68f60a16c8ba5a51d6198d67171cd Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Fri, 3 Jan 2025 11:16:31 +0000 +Subject: [PATCH] net: phylink: add support for PCS supported_interfaces bitmap + +Add support for the PCS to specify which interfaces it supports, which +can be used by MAC drivers to build the main supported_interfaces +bitmap. Phylink also validates that the PCS returned by the MAC driver +supports the interface that the MAC was asked for. + +An empty supported_interfaces bitmap from the PCS indicates that it +does not provide this information, and we handle that appropriately. + +Reviewed-by: Maxime Chevallier +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1tTffL-007RoD-1Y@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 11 +++++++++++ + include/linux/phylink.h | 3 +++ + 2 files changed, 14 insertions(+) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -701,6 +701,17 @@ static int phylink_validate_mac_and_pcs( + return -EINVAL; + } + ++ /* Ensure that this PCS supports the interface which the MAC ++ * returned it for. It is an error for the MAC to return a PCS ++ * that does not support the interface mode. ++ */ ++ if (!phy_interface_empty(pcs->supported_interfaces) && ++ !test_bit(state->interface, pcs->supported_interfaces)) { ++ phylink_err(pl, "MAC returned PCS which does not support %s\n", ++ phy_modes(state->interface)); ++ return -EINVAL; ++ } ++ + /* Validate the link parameters with the PCS */ + if (pcs->ops->pcs_validate) { + ret = pcs->ops->pcs_validate(pcs, supported, state); +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -393,6 +393,8 @@ struct phylink_pcs_ops; + + /** + * struct phylink_pcs - PHYLINK PCS instance ++ * @supported_interfaces: describing which PHY_INTERFACE_MODE_xxx ++ * are supported by this PCS. + * @ops: a pointer to the &struct phylink_pcs_ops structure + * @phylink: pointer to &struct phylink_config + * @neg_mode: provide PCS neg mode via "mode" argument +@@ -409,6 +411,7 @@ struct phylink_pcs_ops; + * the PCS driver. + */ + struct phylink_pcs { ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); + const struct phylink_pcs_ops *ops; + struct phylink *phylink; + bool neg_mode; diff --git a/target/linux/generic/backport-6.12/652-v6.15-net-phylink-force-link-down-on-major_config-failure.patch b/target/linux/generic/backport-6.12/652-v6.15-net-phylink-force-link-down-on-major_config-failure.patch new file mode 100644 index 0000000000..01180b6697 --- /dev/null +++ b/target/linux/generic/backport-6.12/652-v6.15-net-phylink-force-link-down-on-major_config-failure.patch @@ -0,0 +1,125 @@ +From f1ae32a709e0b525d7963207eb3a4747626f4818 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Mon, 24 Mar 2025 16:40:08 +0000 +Subject: [PATCH] net: phylink: force link down on major_config failure + +If we fail to configure the MAC or PCS according to the desired mode, +do not allow the network link to come up until we have successfully +configured the MAC and PCS. This improves phylink's behaviour when an +error occurs. + +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1twkqO-0006FI-Gm@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 42 +++++++++++++++++++++++++++++++-------- + 1 file changed, 34 insertions(+), 8 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -82,6 +82,7 @@ struct phylink { + + bool link_failed; + bool using_mac_select_pcs; ++ bool major_config_failed; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -1377,12 +1378,16 @@ static void phylink_major_config(struct + phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(state->interface)); + ++ pl->major_config_failed = false; ++ + if (pl->using_mac_select_pcs) { + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) { + phylink_err(pl, + "mac_select_pcs unexpectedly failed: %pe\n", + pcs); ++ ++ pl->major_config_failed = true; + return; + } + +@@ -1404,6 +1409,7 @@ static void phylink_major_config(struct + if (err < 0) { + phylink_err(pl, "mac_prepare failed: %pe\n", + ERR_PTR(err)); ++ pl->major_config_failed = true; + return; + } + } +@@ -1427,8 +1433,15 @@ static void phylink_major_config(struct + + phylink_mac_config(pl, state); + +- if (pl->pcs) +- phylink_pcs_post_config(pl->pcs, state->interface); ++ if (pl->pcs) { ++ err = phylink_pcs_post_config(pl->pcs, state->interface); ++ if (err < 0) { ++ phylink_err(pl, "pcs_post_config failed: %pe\n", ++ ERR_PTR(err)); ++ ++ pl->major_config_failed = true; ++ } ++ } + + if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) + phylink_pcs_enable(pl->pcs); +@@ -1439,11 +1452,12 @@ static void phylink_major_config(struct + + err = phylink_pcs_config(pl->pcs, neg_mode, state, + !!(pl->link_config.pause & MLO_PAUSE_AN)); +- if (err < 0) +- phylink_err(pl, "pcs_config failed: %pe\n", +- ERR_PTR(err)); +- else if (err > 0) ++ if (err < 0) { ++ phylink_err(pl, "pcs_config failed: %pe\n", ERR_PTR(err)); ++ pl->major_config_failed = true; ++ } else if (err > 0) { + restart = true; ++ } + + if (restart) + phylink_pcs_an_restart(pl); +@@ -1451,16 +1465,22 @@ static void phylink_major_config(struct + if (pl->mac_ops->mac_finish) { + err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode, + state->interface); +- if (err < 0) ++ if (err < 0) { + phylink_err(pl, "mac_finish failed: %pe\n", + ERR_PTR(err)); ++ ++ pl->major_config_failed = true; ++ } + } + + if (pl->phydev && pl->phy_ib_mode) { + err = phy_config_inband(pl->phydev, pl->phy_ib_mode); +- if (err < 0) ++ if (err < 0) { + phylink_err(pl, "phy_config_inband: %pe\n", + ERR_PTR(err)); ++ ++ pl->major_config_failed = true; ++ } + } + + if (pl->sfp_bus) { +@@ -1762,6 +1782,12 @@ static void phylink_resolve(struct work_ + } + } + ++ /* If configuration of the interface failed, force the link down ++ * until we get a successful configuration. ++ */ ++ if (pl->major_config_failed) ++ link_state.link = false; ++ + if (link_state.link != cur_link_state) { + pl->old_link_state = link_state.link; + if (!link_state.link) diff --git a/target/linux/generic/backport-6.12/653-01-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch b/target/linux/generic/backport-6.12/653-01-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch new file mode 100644 index 0000000000..8533c72707 --- /dev/null +++ b/target/linux/generic/backport-6.12/653-01-v7.0-net-phylink-simplify-phylink_resolve-phylink_major_c.patch @@ -0,0 +1,61 @@ +From 7bf588dc62a05c1866efe098e1b188fd879aa2cf Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 14:19:51 +0200 +Subject: [PATCH] net: phylink: simplify phylink_resolve() -> + phylink_major_config() path + +This is a trivial change with no functional effect which replaces the +pattern: + +if (a) { + if (b) { + do_stuff(); + } +} + +with: + +if (a && b) { + do_stuff(); +}; + +The purpose is to reduce the delta of a subsequent functional change. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20260119121954.1624535-2-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1768,18 +1768,16 @@ static void phylink_resolve(struct work_ + if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + +- if (mac_config) { +- if (link_state.interface != pl->link_config.interface) { +- /* The interface has changed, force the link down and +- * then reconfigure. +- */ +- if (cur_link_state) { +- phylink_link_down(pl); +- cur_link_state = false; +- } +- phylink_major_config(pl, false, &link_state); +- pl->link_config.interface = link_state.interface; ++ if (mac_config && link_state.interface != pl->link_config.interface) { ++ /* The interface has changed, so force the link down and then ++ * reconfigure. ++ */ ++ if (cur_link_state) { ++ phylink_link_down(pl); ++ cur_link_state = false; + } ++ phylink_major_config(pl, false, &link_state); ++ pl->link_config.interface = link_state.interface; + } + + /* If configuration of the interface failed, force the link down diff --git a/target/linux/generic/backport-6.12/653-02-v7.0-net-phylink-introduce-helpers-for-replaying-link-cal.patch b/target/linux/generic/backport-6.12/653-02-v7.0-net-phylink-introduce-helpers-for-replaying-link-cal.patch new file mode 100644 index 0000000000..462848e6ae --- /dev/null +++ b/target/linux/generic/backport-6.12/653-02-v7.0-net-phylink-introduce-helpers-for-replaying-link-cal.patch @@ -0,0 +1,148 @@ +From 96969b132bf1a5b875ab84fcb41a5c4972c3be9e Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean +Date: Mon, 19 Jan 2026 14:19:52 +0200 +Subject: [PATCH 2/2] net: phylink: introduce helpers for replaying link + callbacks + +Some drivers of MAC + tightly integrated PCS (example: SJA1105 + XPCS +covered by same reset domain) need to perform resets at runtime. + +The reset is triggered by the MAC driver, and it needs to restore its +and the PCS' registers, all invisible to phylink. + +However, there is a desire to simplify the API through which the MAC and +the PCS interact, so this becomes challenging. + +Phylink holds all the necessary state to help with this operation, and +can offer two helpers which walk the MAC and PCS drivers again through +the callbacks required during a destructive reset operation. The +procedure is as follows: + +Before reset, MAC driver calls phylink_replay_link_begin(): +- Triggers phylink mac_link_down() and pcs_link_down() methods + +After reset, MAC driver calls phylink_replay_link_end(): +- Triggers phylink mac_config() -> pcs_config() -> mac_link_up() -> + pcs_link_up() methods. + +MAC and PCS registers are restored with no other custom driver code. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Russell King (Oracle) +Link: https://patch.msgid.link/20260119121954.1624535-3-vladimir.oltean@nxp.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 61 +++++++++++++++++++++++++++++++++++++-- + include/linux/phylink.h | 5 ++++ + 2 files changed, 63 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -34,6 +34,7 @@ enum { + PHYLINK_DISABLE_STOPPED, + PHYLINK_DISABLE_LINK, + PHYLINK_DISABLE_MAC_WOL, ++ PHYLINK_DISABLE_REPLAY, + + PCS_STATE_DOWN = 0, + PCS_STATE_STARTING, +@@ -83,6 +84,7 @@ struct phylink { + bool link_failed; + bool using_mac_select_pcs; + bool major_config_failed; ++ bool force_major_config; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -1768,9 +1770,10 @@ static void phylink_resolve(struct work_ + if (pl->act_link_an_mode != MLO_AN_FIXED) + phylink_apply_manual_flow(pl, &link_state); + +- if (mac_config && link_state.interface != pl->link_config.interface) { +- /* The interface has changed, so force the link down and then +- * reconfigure. ++ if ((mac_config && link_state.interface != pl->link_config.interface) || ++ pl->force_major_config) { ++ /* The interface has changed or a forced major configuration ++ * was requested, so force the link down and then reconfigure. + */ + if (cur_link_state) { + phylink_link_down(pl); +@@ -1778,6 +1781,7 @@ static void phylink_resolve(struct work_ + } + phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; ++ pl->force_major_config = false; + } + + /* If configuration of the interface failed, force the link down +@@ -4130,6 +4134,57 @@ void phylink_mii_c45_pcs_get_state(struc + } + EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); + ++/** ++ * phylink_replay_link_begin() - begin replay of link callbacks for driver ++ * which loses state ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * ++ * Helper for MAC drivers which may perform a destructive reset at runtime. ++ * Both the own driver's mac_link_down() method is called, as well as the ++ * pcs_link_down() method of the split PCS (if any). ++ * ++ * This is similar to phylink_stop(), except it does not alter the state of ++ * the phylib PHY (it is assumed that it is not affected by the MAC destructive ++ * reset). ++ */ ++void phylink_replay_link_begin(struct phylink *pl) ++{ ++ ASSERT_RTNL(); ++ ++ phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_REPLAY); ++} ++EXPORT_SYMBOL_GPL(phylink_replay_link_begin); ++ ++/** ++ * phylink_replay_link_end() - end replay of link callbacks for driver ++ * which lost state ++ * @pl: a pointer to a &struct phylink returned from phylink_create() ++ * ++ * Helper for MAC drivers which may perform a destructive reset at runtime. ++ * Both the own driver's mac_config() and mac_link_up() methods, as well as the ++ * pcs_config() and pcs_link_up() method of the split PCS (if any), are called. ++ * ++ * This is similar to phylink_start(), except it does not alter the state of ++ * the phylib PHY. ++ * ++ * One must call this method only within the same rtnl_lock() critical section ++ * as a previous phylink_replay_link_start(). ++ */ ++void phylink_replay_link_end(struct phylink *pl) ++{ ++ ASSERT_RTNL(); ++ ++ if (WARN(!test_bit(PHYLINK_DISABLE_REPLAY, ++ &pl->phylink_disable_state), ++ "phylink_replay_link_end() called without a prior phylink_replay_link_begin()\n")) ++ return; ++ ++ pl->force_major_config = true; ++ phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_REPLAY); ++ flush_work(&pl->resolve); ++} ++EXPORT_SYMBOL_GPL(phylink_replay_link_end); ++ + static int __init phylink_init(void) + { + for (int i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); ++i) +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -707,4 +707,9 @@ void phylink_mii_c45_pcs_get_state(struc + + void phylink_decode_usxgmii_word(struct phylink_link_state *state, + uint16_t lpa); ++ ++void phylink_replay_link_begin(struct phylink *pl); ++ ++void phylink_replay_link_end(struct phylink *pl); ++ + #endif diff --git a/target/linux/generic/backport-6.12/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch b/target/linux/generic/backport-6.12/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch new file mode 100644 index 0000000000..c579ffc6a6 --- /dev/null +++ b/target/linux/generic/backport-6.12/702-v7.0-net-phylink-fix-NULL-pointer-deref-in-phylink_major_.patch @@ -0,0 +1,79 @@ +From 0e4d7df2f3b2e59c1bccc09ea099b7a6c2dda886 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Wed, 28 Jan 2026 10:51:56 +0000 +Subject: [PATCH] net: phylink: fix NULL pointer deref in + phylink_major_config() + +When a MAC driver returns a PCS for an interface mode, and then we +attempt to switch to a different mode that doesn't require a PCS, +this causes phylink to oops: + +Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010 +Mem abort info: + ESR = 0x0000000096000044 + EC = 0x25: DABT (current EL), IL = 32 bits + SET = 0, FnV = 0 + EA = 0, S1PTW = 0 + FSC = 0x04: level 0 translation fault +Data abort info: + ISV = 0, ISS = 0x00000044, ISS2 = 0x00000000 + CM = 0, WnR = 1, TnD = 0, TagAccess = 0 + GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 +user pgtable: 4k pages, 48-bit VAs, pgdp=0000000137f96000 +[0000000000000010] pgd=0000000000000000, p4d=0000000000000000 +Internal error: Oops: 0000000096000044 [#1] SMP +Modules linked in: -- +CPU: 1 UID: 0 PID: 55 Comm: kworker/u33:0 Not tainted 6.19.0-rc5-00581-g73cb8467a63e #1 PREEMPT +Hardware name: Qualcomm Technologies, Inc. Lemans Ride Rev3 (DT) +Workqueue: events_power_efficient phylink_resolve +pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS +BTYPE=--) +pc : phylink_major_config+0x408/0x948 +lr : phylink_major_config+0x3fc/0x948 +sp : ffff800080353c60 +x29: ffff800080353cb0 x28: ffffb305068a8a00 x27: ffffb305068a8000 +x26: ffff000080092100 x25: 0000000000000000 x24: 0000000000000000 +x23: 0000000000000001 x22: 0000000000000000 x21: ffffb3050555b3d0 +x20: ffff800080353d10 x19: ffff0000b6059400 x18: 00000000ffffffff +x17: 74756f2f79687020 x16: ffffb305045e4f18 x15: 6769666e6f632072 +x14: 6f6a616d203a3168 x13: 782d657361623030 x12: ffffb305068c6a98 +x11: 0000000000000583 x10: 0000000000000018 x9 : ffffb305068c6a98 +x8 : 0000000100006583 x7 : 0000000000000000 x6 : ffff00008083cc40 +x5 : ffff00008083cc40 x4 : 0000000000000001 x3 : 0000000000000001 +x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff0000b269e5a8 +Call trace: + phylink_major_config+0x408/0x948 (P) + phylink_resolve+0x294/0x6e4 + process_one_work+0x148/0x28c + worker_thread+0x2d8/0x3d8 + kthread+0x134/0x208 + ret_from_fork+0x10/0x20 +Code: d63f0020 f9400e60 b4000040 f900081f (f9000ad3) +---[ end trace 0000000000000000 ]--- + +This is caused by "pcs" being NULL when we attempt to execute: + + pcs->phylink = pl; + +Make this conditional on pcs being non-null. + +Fixes: 486dc391ef43 ("net: phylink: allow mac_select_pcs() to remove a PCS") +Reported-by: Mohd Ayaan Anwar +Signed-off-by: Russell King (Oracle) +Link: https://patch.msgid.link/E1vl39Q-00000006utm-229h@rmk-PC.armlinux.org.uk +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phylink.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1425,7 +1425,8 @@ static void phylink_major_config(struct + if (pl->pcs) + pl->pcs->phylink = NULL; + +- pcs->phylink = pl; ++ if (pcs) ++ pcs->phylink = pl; + + pl->pcs = pcs; + } diff --git a/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch b/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch index dea84c7079..b60045b1e7 100644 --- a/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch +++ b/target/linux/generic/pending-6.12/706-net-phy-populate-host_interfaces-when-attaching-PHY.patch @@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -2273,7 +2273,7 @@ int phylink_fwnode_phy_connect(struct ph +@@ -2313,7 +2313,7 @@ int phylink_fwnode_phy_connect(struct ph { struct fwnode_handle *phy_fwnode; struct phy_device *phy_dev; @@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle /* Fixed links and 802.3z are handled without needing a PHY */ if (pl->cfg_link_an_mode == MLO_AN_FIXED || -@@ -2303,6 +2303,25 @@ int phylink_fwnode_phy_connect(struct ph +@@ -2343,6 +2343,25 @@ int phylink_fwnode_phy_connect(struct ph if (pl->config->mac_requires_rxc) flags |= PHY_F_RXC_ALWAYS_ON; diff --git a/target/linux/generic/pending-6.12/770-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch b/target/linux/generic/pending-6.12/770-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch new file mode 100644 index 0000000000..6ea8d57aa9 --- /dev/null +++ b/target/linux/generic/pending-6.12/770-01-net-phylink-keep-and-use-MAC-supported_interfaces-in.patch @@ -0,0 +1,88 @@ +From c6f6cb55c3c316f6169e07eacc5ccb214116719a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 15:40:12 +0200 +Subject: [PATCH 1/7] net: phylink: keep and use MAC supported_interfaces in + phylink struct + +Add in phylink struct a copy of supported_interfaces from phylink_config +and make use of that instead of relying on phylink_config value. + +This in preparation for support of PCS handling internally to phylink +where a PCS can be removed or added after the phylink is created and we +need both a reference of the supported_interfaces value from +phylink_config and an internal value that can be updated with the new +PCS info. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 22 +++++++++++++++------- + 1 file changed, 15 insertions(+), 7 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -65,6 +65,11 @@ struct phylink { + /* The link configuration settings */ + struct phylink_link_state link_config; + ++ /* What interface are supported by the current link. ++ * Can change on removal or addition of new PCS. ++ */ ++ DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ + /* The current settings */ + phy_interface_t cur_interface; + +@@ -793,7 +798,7 @@ static int phylink_validate_mask(struct + static int phylink_validate(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state) + { +- const unsigned long *interfaces = pl->config->supported_interfaces; ++ const unsigned long *interfaces = pl->supported_interfaces; + + if (state->interface == PHY_INTERFACE_MODE_NA) + return phylink_validate_mask(pl, NULL, supported, state, +@@ -1948,6 +1953,9 @@ struct phylink *phylink_create(struct ph + mutex_init(&pl->state_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); + ++ phy_interface_copy(pl->supported_interfaces, ++ config->supported_interfaces); ++ + pl->config = config; + if (config->type == PHYLINK_NETDEV) { + pl->netdev = to_net_dev(config->dev); +@@ -2091,7 +2099,7 @@ static int phylink_validate_phy(struct p + * those which the host supports. + */ + phy_interface_and(interfaces, phy->possible_interfaces, +- pl->config->supported_interfaces); ++ pl->supported_interfaces); + + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "PHY has no common interfaces\n"); +@@ -3565,14 +3573,14 @@ static int phylink_sfp_config_optical(st + + phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n", + (int)PHY_INTERFACE_MODE_MAX, +- pl->config->supported_interfaces, ++ pl->supported_interfaces, + (int)PHY_INTERFACE_MODE_MAX, + pl->sfp_interfaces); + + /* Find the union of the supported interfaces by the PCS/MAC and + * the SFP module. + */ +- phy_interface_and(interfaces, pl->config->supported_interfaces, ++ phy_interface_and(interfaces, pl->supported_interfaces, + pl->sfp_interfaces); + if (phy_interface_empty(interfaces)) { + phylink_err(pl, "unsupported SFP module: no common interface modes\n"); +@@ -3730,7 +3738,7 @@ static int phylink_sfp_connect_phy(void + + /* Set the PHY's host supported interfaces */ + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, +- pl->config->supported_interfaces); ++ pl->supported_interfaces); + + /* Do the initial configuration */ + ret = phylink_sfp_config_phy(pl, phy); diff --git a/target/linux/generic/pending-6.12/770-02-net-phylink-introduce-internal-phylink-PCS-handling.patch b/target/linux/generic/pending-6.12/770-02-net-phylink-introduce-internal-phylink-PCS-handling.patch new file mode 100644 index 0000000000..d7c00f38af --- /dev/null +++ b/target/linux/generic/pending-6.12/770-02-net-phylink-introduce-internal-phylink-PCS-handling.patch @@ -0,0 +1,376 @@ +From d134e22b540226a7404cabb88c86a54857486b4f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 16:03:26 +0200 +Subject: [PATCH 2/7] net: phylink: introduce internal phylink PCS handling + +Introduce internal handling of PCS for phylink. This is an alternative +to .mac_select_pcs that moves the selection logic of the PCS entirely to +phylink with the usage of supported_interface value in the PCS struct. + +MAC should now provide an array of available PCS in phylink_config in +.available_pcs and fill the .num_available_pcs with the number of +elements in the array. MAC should also define a new bitmap, +pcs_interfaces, in phylink_config to define for what interface mode a +dedicated PCS is required. + +On phylink_create() this array is parsed and a linked list of PCS is +created based on the PCS passed in phylink_config. +Also the supported_interface value in phylink struct is updated with the +new supported_interface from the provided PCS. + +On phylink_start() every PCS in phylink PCS list gets attached to the +phylink instance. This is done by setting the phylink value in +phylink_pcs struct to the phylink instance. + +On phylink_stop(), every PCS in phylink PCS list is detached from the +phylink instance. This is done by setting the phylink value in +phylink_pcs struct to NULL. + +On phylink_stop(), every PCS in phylink PCS list is removed from the +list. + +phylink_validate_mac_and_pcs(), phylink_major_config() and +phylink_inband_caps() are updated to support this new implementation +with the PCS list stored in phylink. + +They will make use of phylink_validate_pcs_interface() that will loop +for every PCS in the phylink PCS available list and find one that supports +the passed interface. + +phylink_validate_pcs_interface() apply the same logic of .mac_select_pcs +where if a supported_interface value is not set for the PCS struct, then +it's assumed every interface is supported. + +It's required for a MAC that implement either a .mac_select_pcs or make +use of the PCS list implementation. Implementing both will result in a fail +on MAC/PCS validation. + +phylink value in phylink_pcs struct with this implementation is used to +track from PCS side when it's attached to a phylink instance. PCS driver +will make use of this information to correctly detach from a phylink +instance if needed. + +The .mac_select_pcs implementation is not changed but it's expected that +every MAC driver migrates to the new implementation to later deprecate +and remove .mac_select_pcs. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 149 +++++++++++++++++++++++++++++++++----- + include/linux/phylink.h | 11 +++ + 2 files changed, 141 insertions(+), 19 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -65,6 +65,9 @@ struct phylink { + /* The link configuration settings */ + struct phylink_link_state link_config; + ++ /* List of available PCS */ ++ struct list_head pcs_list; ++ + /* What interface are supported by the current link. + * Can change on removal or addition of new PCS. + */ +@@ -144,6 +147,8 @@ static const phy_interface_t phylink_sfp + + static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces); + ++static void phylink_run_resolve(struct phylink *pl); ++ + /** + * phylink_set_port_modes() - set the port type modes in the ethtool mask + * @mask: ethtool link mode mask +@@ -680,24 +685,59 @@ static void phylink_validate_mask_caps(u + linkmode_and(state->advertising, state->advertising, mask); + } + ++static int phylink_validate_pcs_interface(struct phylink_pcs *pcs, ++ phy_interface_t interface) ++{ ++ /* If PCS define an empty supported_interfaces value, assume ++ * all interface are supported. ++ */ ++ if (phy_interface_empty(pcs->supported_interfaces)) ++ return 0; ++ ++ /* Ensure that this PCS supports the interface mode */ ++ if (!test_bit(interface, pcs->supported_interfaces)) ++ return -EINVAL; ++ ++ return 0; ++} ++ + static int phylink_validate_mac_and_pcs(struct phylink *pl, + unsigned long *supported, + struct phylink_link_state *state) + { + unsigned long capabilities; + struct phylink_pcs *pcs; ++ bool pcs_found = false; + int ret; + + /* Get the PCS for this interface mode */ +- if (pl->using_mac_select_pcs) { ++ if (pl->mac_ops->mac_select_pcs) { ++ /* Make sure either PCS internal validation or .mac_select_pcs ++ * is used. Return error if both are defined. ++ */ ++ if (!list_empty(&pl->pcs_list)) { ++ phylink_err(pl, "either phylink_pcs_add() or .mac_select_pcs must be used\n"); ++ return -EINVAL; ++ } ++ + pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); ++ ++ pcs_found = !!pcs; + } else { +- pcs = pl->pcs; ++ /* Check every assigned PCS and search for one that supports ++ * the interface. ++ */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, state->interface)) { ++ pcs_found = true; ++ break; ++ } ++ } + } + +- if (pcs) { ++ if (pcs_found) { + /* The PCS, if present, must be setup before phylink_create() + * has been called. If the ops is not initialised, print an + * error and backtrace rather than oopsing the kernel. +@@ -709,13 +749,10 @@ static int phylink_validate_mac_and_pcs( + return -EINVAL; + } + +- /* Ensure that this PCS supports the interface which the MAC +- * returned it for. It is an error for the MAC to return a PCS +- * that does not support the interface mode. +- */ +- if (!phy_interface_empty(pcs->supported_interfaces) && +- !test_bit(state->interface, pcs->supported_interfaces)) { +- phylink_err(pl, "MAC returned PCS which does not support %s\n", ++ /* Recheck PCS to handle legacy way for .mac_select_pcs */ ++ ret = phylink_validate_pcs_interface(pcs, state->interface); ++ if (ret) { ++ phylink_err(pl, "selected PCS does not support %s\n", + phy_modes(state->interface)); + return -EINVAL; + } +@@ -1096,12 +1133,22 @@ static unsigned int phylink_inband_caps( + phy_interface_t interface) + { + struct phylink_pcs *pcs; ++ bool pcs_found = false; + +- if (!pl->mac_ops->mac_select_pcs) +- return 0; ++ if (pl->mac_ops->mac_select_pcs) { ++ pcs = pl->mac_ops->mac_select_pcs(pl->config, ++ interface); ++ pcs_found = !!pcs; ++ } else { ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, interface)) { ++ pcs_found = true; ++ break; ++ } ++ } ++ } + +- pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); +- if (!pcs) ++ if (!pcs_found) + return 0; + + return phylink_pcs_inband_caps(pcs, interface); +@@ -1397,10 +1444,36 @@ static void phylink_major_config(struct + pl->major_config_failed = true; + return; + } ++ /* Find a PCS in available PCS list for the requested interface. ++ * This doesn't overwrite the previous .mac_select_pcs as either ++ * .mac_select_pcs or PCS list implementation are permitted. ++ * ++ * Skip searching if the MAC doesn't require a dedicaed PCS for ++ * the requested interface. ++ */ ++ } else if (test_bit(state->interface, pl->config->pcs_interfaces)) { ++ bool pcs_found = false; ++ ++ list_for_each_entry(pcs, &pl->pcs_list, list) { ++ if (!phylink_validate_pcs_interface(pcs, ++ state->interface)) { ++ pcs_found = true; ++ break; ++ } ++ } ++ ++ if (!pcs_found) { ++ phylink_err(pl, ++ "couldn't find a PCS for %s\n", ++ phy_modes(state->interface)); + +- pcs_changed = pl->pcs != pcs; ++ pl->major_config_failed = true; ++ return; ++ } + } + ++ pcs_changed = pl->pcs != pcs; ++ + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); + + phylink_dbg(pl, "major config, active %s/%s/%s\n", +@@ -1427,11 +1500,13 @@ static void phylink_major_config(struct + if (pcs_changed) { + phylink_pcs_disable(pl->pcs); + +- if (pl->pcs) +- pl->pcs->phylink = NULL; ++ if (pl->mac_ops->mac_select_pcs) { ++ if (pl->pcs) ++ pl->pcs->phylink = NULL; + +- if (pcs) +- pcs->phylink = pl; ++ if (pcs) ++ pcs->phylink = pl; ++ } + + pl->pcs = pcs; + } +@@ -1931,8 +2006,9 @@ struct phylink *phylink_create(struct ph + const struct phylink_mac_ops *mac_ops) + { + bool using_mac_select_pcs = false; ++ struct phylink_pcs *pcs; + struct phylink *pl; +- int ret; ++ int i, ret; + + /* Validate the supplied configuration */ + if (phy_interface_empty(config->supported_interfaces)) { +@@ -1952,9 +2028,21 @@ struct phylink *phylink_create(struct ph + + mutex_init(&pl->state_mutex); + INIT_WORK(&pl->resolve, phylink_resolve); ++ INIT_LIST_HEAD(&pl->pcs_list); ++ ++ /* Fill the PCS list with available PCS from phylink config */ ++ for (i = 0; i < config->num_available_pcs; i++) { ++ pcs = config->available_pcs[i]; ++ ++ list_add(&pcs->list, &pl->pcs_list); ++ } + + phy_interface_copy(pl->supported_interfaces, + config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); + + pl->config = config; + if (config->type == PHYLINK_NETDEV) { +@@ -2024,10 +2112,16 @@ EXPORT_SYMBOL_GPL(phylink_create); + */ + void phylink_destroy(struct phylink *pl) + { ++ struct phylink_pcs *pcs, *tmp; ++ + sfp_bus_del_upstream(pl->sfp_bus); + if (pl->link_gpio) + gpiod_put(pl->link_gpio); + ++ /* Remove every PCS from phylink PCS list */ ++ list_for_each_entry_safe(pcs, tmp, &pl->pcs_list, list) ++ list_del(&pcs->list); ++ + cancel_work_sync(&pl->resolve); + kfree(pl); + } +@@ -2472,6 +2566,7 @@ static irqreturn_t phylink_link_handler( + */ + void phylink_start(struct phylink *pl) + { ++ struct phylink_pcs *pcs; + bool poll = false; + + ASSERT_RTNL(); +@@ -2498,6 +2593,10 @@ void phylink_start(struct phylink *pl) + + pl->pcs_state = PCS_STATE_STARTED; + ++ /* link available PCS to phylink struct */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ pcs->phylink = pl; ++ + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); + + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { +@@ -2542,6 +2641,8 @@ EXPORT_SYMBOL_GPL(phylink_start); + */ + void phylink_stop(struct phylink *pl) + { ++ struct phylink_pcs *pcs; ++ + ASSERT_RTNL(); + + if (pl->sfp_bus) +@@ -2559,6 +2660,14 @@ void phylink_stop(struct phylink *pl) + pl->pcs_state = PCS_STATE_DOWN; + + phylink_pcs_disable(pl->pcs); ++ ++ /* Drop link between phylink and PCS */ ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ pcs->phylink = NULL; ++ ++ /* Restore original supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); + } + EXPORT_SYMBOL_GPL(phylink_stop); + +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -147,7 +147,11 @@ enum phylink_op_type { + * if MAC link is at %MLO_AN_FIXED mode. + * @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx + * are supported by the MAC/PCS. ++ * @pcs_interfaces: bitmap describing for which PHY_INTERFACE_MODE_xxx a ++ * dedicated PCS is required. + * @mac_capabilities: MAC pause/speed/duplex capabilities. ++ * @available_pcs: array of available phylink_pcs PCS ++ * @num_available_pcs: num of available phylink_pcs PCS + */ + struct phylink_config { + struct device *dev; +@@ -159,7 +163,11 @@ struct phylink_config { + void (*get_fixed_state)(struct phylink_config *config, + struct phylink_link_state *state); + DECLARE_PHY_INTERFACE_MASK(supported_interfaces); ++ DECLARE_PHY_INTERFACE_MASK(pcs_interfaces); + unsigned long mac_capabilities; ++ ++ struct phylink_pcs **available_pcs; ++ unsigned int num_available_pcs; + }; + + void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed); +@@ -417,6 +425,9 @@ struct phylink_pcs { + bool neg_mode; + bool poll; + bool rxc_always_on; ++ ++ /* private: */ ++ struct list_head list; + }; + + /** diff --git a/target/linux/generic/pending-6.12/770-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch b/target/linux/generic/pending-6.12/770-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch new file mode 100644 index 0000000000..72d920c241 --- /dev/null +++ b/target/linux/generic/pending-6.12/770-03-net-phylink-add-phylink_release_pcs-to-externally-re.patch @@ -0,0 +1,134 @@ +From 1cb4e56c3ba32ac1bce89dc9c34ef2dbc9b89ad4 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 31 Mar 2025 19:10:24 +0200 +Subject: [PATCH 3/7] net: phylink: add phylink_release_pcs() to externally + release a PCS + +Add phylink_release_pcs() to externally release a PCS from a phylink +instance. This can be used to handle case when a single PCS needs to be +removed and the phylink instance needs to be refreshed. + +On calling phylink_release_pcs(), the PCS will be removed from the +phylink internal PCS list and the phylink supported_interfaces value is +reparsed with the remaining PCS interfaces. + +Also a phylink resolve is triggered to handle the PCS removal. + +It's also added to phylink a flag to make phylink resolve reconfigure +the interface mode (even if it didn't change). This is needed to handle +the special case when the current PCS used by phylink is removed and a +major_config is needed to propagae the configuration change. With this +option enabled we also force mac_config even if the PHY link is not up +for the in-band case. + +Signed-off-by: Christian Marangi +--- + drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++- + include/linux/phylink.h | 2 ++ + 2 files changed, 58 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -93,6 +93,7 @@ struct phylink { + bool using_mac_select_pcs; + bool major_config_failed; + bool force_major_config; ++ bool reconfig_interface; + + struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; +@@ -1064,6 +1065,55 @@ static void phylink_resolve_an_pause(str + } + } + ++/** ++ * phylink_release_pcs - Removes a PCS from the phylink PCS available list ++ * @pcs: a pointer to the phylink_pcs struct to be released ++ * ++ * This function release a PCS from the phylink PCS available list if ++ * actually in use. It also refreshes the supported interfaces of the ++ * phylink instance by copying the supported interfaces from the phylink ++ * conf and merging the supported interfaces of the remaining available PCS ++ * in the list and trigger a resolve. ++ */ ++void phylink_release_pcs(struct phylink_pcs *pcs) ++{ ++ struct phylink *pl; ++ ++ ASSERT_RTNL(); ++ ++ pl = pcs->phylink; ++ if (!pl) ++ return; ++ ++ list_del(&pcs->list); ++ pcs->phylink = NULL; ++ ++ /* Check if we are removing the PCS currently ++ * in use by phylink. If this is the case, ++ * force phylink resolve to reconfigure the interface ++ * mode and set the phylink PCS to NULL. ++ */ ++ if (pl->pcs == pcs) { ++ mutex_lock(&pl->state_mutex); ++ ++ pl->reconfig_interface = true; ++ pl->pcs = NULL; ++ ++ mutex_unlock(&pl->state_mutex); ++ } ++ ++ /* Refresh supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); ++ ++ phylink_run_resolve(pl); ++} ++EXPORT_SYMBOL_GPL(phylink_release_pcs); ++ + static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) + { +@@ -1818,6 +1868,10 @@ static void phylink_resolve(struct work_ + if (pl->phydev) + link_state.link &= pl->phy_state.link; + ++ /* Force mac_config if we need to reconfig the interface */ ++ if (pl->reconfig_interface) ++ mac_config = true; ++ + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { + /* If the interface has changed, force a link down +@@ -1852,7 +1906,7 @@ static void phylink_resolve(struct work_ + phylink_apply_manual_flow(pl, &link_state); + + if ((mac_config && link_state.interface != pl->link_config.interface) || +- pl->force_major_config) { ++ pl->force_major_config || pl->reconfig_interface) { + /* The interface has changed or a forced major configuration + * was requested, so force the link down and then reconfigure. + */ +@@ -1863,6 +1917,7 @@ static void phylink_resolve(struct work_ + phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; + pl->force_major_config = false; ++ pl->reconfig_interface = false; + } + + /* If configuration of the interface failed, force the link down +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -632,6 +632,8 @@ void phylink_disconnect_phy(struct phyli + int phylink_set_fixed_link(struct phylink *, + const struct phylink_link_state *); + ++void phylink_release_pcs(struct phylink_pcs *pcs); ++ + void phylink_mac_change(struct phylink *, bool up); + void phylink_pcs_change(struct phylink_pcs *, bool up); + diff --git a/target/linux/generic/pending-6.12/770-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch b/target/linux/generic/pending-6.12/770-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch new file mode 100644 index 0000000000..676e23c001 --- /dev/null +++ b/target/linux/generic/pending-6.12/770-04-net-pcs-implement-Firmware-node-support-for-PCS-driv.patch @@ -0,0 +1,384 @@ +From a90c644c73bbffd400cd3839fc17ffdfc69ea1e8 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 17 Mar 2025 01:46:53 +0100 +Subject: [PATCH 4/7] net: pcs: implement Firmware node support for PCS driver + +Implement the foundation of Firmware node support for PCS driver. + +To support this, implement a simple Provider API where a PCS driver can +expose multiple PCS with an xlate .get function. + +PCS driver will have to call fwnode_pcs_add_provider() and pass the +firmware node pointer and a xlate function to return the correct PCS for +the passed #pcs-cells. + +This will register the PCS in a global list of providers so that +consumer can access it. + +Consumer will then use fwnode_pcs_get() to get the actual PCS by passing +the firmware node pointer and the index for #pcs-cells. + +For simple implementation where #pcs-cells is 0 and the PCS driver +expose a single PCS, the xlate function fwnode_pcs_simple_get() is +provided. + +For advanced implementation a custom xlate function is required. + +PCS driver on removal should first delete as a provider with +the usage of fwnode_pcs_del_provider() and then call +phylink_release_pcs() on every PCS the driver provides and + +A generic function fwnode_phylink_pcs_parse() is provided for any MAC +driver that will declare PCS in DT (or ACPI). +This function will parse "pcs-handle" property and fill the passed array +with the parsed PCS in availabel_pcs up to the passed num_pcs value. +It's also possible to pass NULL as array to only parse the PCS and +update the num_pcs value with the count of scanned PCS. + +Co-developed-by: Daniel Golle +Signed-off-by: Daniel Golle +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/Kconfig | 7 ++ + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs.c | 201 +++++++++++++++++++++++++++++++ + include/linux/pcs/pcs-provider.h | 41 +++++++ + include/linux/pcs/pcs.h | 56 +++++++++ + 5 files changed, 306 insertions(+) + create mode 100644 drivers/net/pcs/pcs.c + create mode 100644 include/linux/pcs/pcs-provider.h + create mode 100644 include/linux/pcs/pcs.h + +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -5,6 +5,13 @@ + + menu "PCS device drivers" + ++config FWNODE_PCS ++ tristate ++ depends on (ACPI || OF) ++ depends on PHYLINK ++ help ++ Firmware node PCS accessors ++ + config PCS_XPCS + tristate "Synopsys DesignWare Ethernet XPCS" + select PHYLINK +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + # Makefile for Linux PCS drivers + ++obj-$(CONFIG_FWNODE_PCS) += pcs.o + pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-plat.o \ + pcs-xpcs-nxp.o pcs-xpcs-wx.o + +--- /dev/null ++++ b/drivers/net/pcs/pcs.c +@@ -0,0 +1,201 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("PCS library"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); ++ ++struct fwnode_pcs_provider { ++ struct list_head link; ++ ++ struct fwnode_handle *fwnode; ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data); ++ ++ void *data; ++}; ++ ++static LIST_HEAD(fwnode_pcs_providers); ++static DEFINE_MUTEX(fwnode_pcs_mutex); ++ ++struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, ++ void *data) ++{ ++ return data; ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_simple_get); ++ ++int fwnode_pcs_add_provider(struct fwnode_handle *fwnode, ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data), ++ void *data) ++{ ++ struct fwnode_pcs_provider *pp; ++ ++ if (!fwnode) ++ return 0; ++ ++ pp = kzalloc(sizeof(*pp), GFP_KERNEL); ++ if (!pp) ++ return -ENOMEM; ++ ++ pp->fwnode = fwnode_handle_get(fwnode); ++ pp->data = data; ++ pp->get = get; ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_add(&pp->link, &fwnode_pcs_providers); ++ mutex_unlock(&fwnode_pcs_mutex); ++ pr_debug("Added pcs provider from %pfwf\n", fwnode); ++ ++ fwnode_dev_initialized(fwnode, true); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider); ++ ++void fwnode_pcs_del_provider(struct fwnode_handle *fwnode) ++{ ++ struct fwnode_pcs_provider *pp; ++ ++ if (!fwnode) ++ return; ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_for_each_entry(pp, &fwnode_pcs_providers, link) { ++ if (pp->fwnode == fwnode) { ++ list_del(&pp->link); ++ fwnode_dev_initialized(pp->fwnode, false); ++ fwnode_handle_put(pp->fwnode); ++ kfree(pp); ++ break; ++ } ++ } ++ mutex_unlock(&fwnode_pcs_mutex); ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_del_provider); ++ ++static int fwnode_parse_pcsspec(const struct fwnode_handle *fwnode, int index, ++ const char *name, ++ struct fwnode_reference_args *out_args) ++{ ++ int ret = -ENOENT; ++ ++ if (!fwnode) ++ return -ENOENT; ++ ++ if (name) ++ index = fwnode_property_match_string(fwnode, "pcs-names", ++ name); ++ ++ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle", ++ "#pcs-cells", ++ -1, index, out_args); ++ if (ret || (name && index < 0)) ++ return ret; ++ ++ return 0; ++} ++ ++static struct phylink_pcs * ++fwnode_pcs_get_from_pcsspec(struct fwnode_reference_args *pcsspec) ++{ ++ struct fwnode_pcs_provider *provider; ++ struct phylink_pcs *pcs = ERR_PTR(-EPROBE_DEFER); ++ ++ if (!pcsspec) ++ return ERR_PTR(-EINVAL); ++ ++ mutex_lock(&fwnode_pcs_mutex); ++ list_for_each_entry(provider, &fwnode_pcs_providers, link) { ++ if (provider->fwnode == pcsspec->fwnode) { ++ pcs = provider->get(pcsspec, provider->data); ++ if (!IS_ERR(pcs)) ++ break; ++ } ++ } ++ mutex_unlock(&fwnode_pcs_mutex); ++ ++ return pcs; ++} ++ ++static struct phylink_pcs *__fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index, const char *con_id) ++{ ++ struct fwnode_reference_args pcsspec; ++ struct phylink_pcs *pcs; ++ int ret; ++ ++ ret = fwnode_parse_pcsspec(fwnode, index, con_id, &pcsspec); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ pcs = fwnode_pcs_get_from_pcsspec(&pcsspec); ++ fwnode_handle_put(pcsspec.fwnode); ++ ++ return pcs; ++} ++ ++struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, int index) ++{ ++ return __fwnode_pcs_get(fwnode, index, NULL); ++} ++EXPORT_SYMBOL_GPL(fwnode_pcs_get); ++ ++static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode, ++ unsigned int *num_pcs) ++{ ++ struct fwnode_reference_args out_args; ++ int index = 0; ++ int ret; ++ ++ while (true) { ++ ret = fwnode_property_get_reference_args(fwnode, "pcs-handle", ++ "#pcs-cells", ++ -1, index, &out_args); ++ /* We expect to reach an -ENOENT error while counting */ ++ if (ret) ++ break; ++ ++ fwnode_handle_put(out_args.fwnode); ++ index++; ++ } ++ ++ /* Update num_pcs with parsed PCS */ ++ *num_pcs = index; ++ ++ /* Return error if we didn't found any PCS */ ++ return index > 0 ? 0 : -ENOENT; ++} ++ ++int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs) ++{ ++ int i; ++ ++ if (!fwnode_property_present(fwnode, "pcs-handle")) ++ return -ENODEV; ++ ++ /* With available_pcs NULL, only count the PCS */ ++ if (!available_pcs) ++ return fwnode_phylink_pcs_count(fwnode, num_pcs); ++ ++ for (i = 0; i < *num_pcs; i++) { ++ struct phylink_pcs *pcs; ++ ++ pcs = fwnode_pcs_get(fwnode, i); ++ if (IS_ERR(pcs)) ++ return PTR_ERR(pcs); ++ ++ available_pcs[i] = pcs; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_parse); +--- /dev/null ++++ b/include/linux/pcs/pcs-provider.h +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#ifndef __LINUX_PCS_PROVIDER_H ++#define __LINUX_PCS_PROVIDER_H ++ ++/** ++ * fwnode_pcs_simple_get - Simple xlate function to retrieve PCS ++ * @pcsspec: reference arguments ++ * @data: Context data (assumed assigned to the single PCS) ++ * ++ * Returns the PCS. (pointed by data) ++ */ ++struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, ++ void *data); ++ ++/** ++ * fwnode_pcs_add_provider - Registers a new PCS provider ++ * @np: Firmware node ++ * @get: xlate function to retrieve the PCS ++ * @data: Context data ++ * ++ * Register and add a new PCS to the global providers list ++ * for the firmware node. A function to get the PCS from ++ * firmware node with the use fwnode reference arguments. ++ * To the get function is also passed the interface type ++ * requested for the PHY. PCS driver will use the passed ++ * interface to understand if the PCS can support it or not. ++ * ++ * Returns 0 on success or -ENOMEM on allocation failure. ++ */ ++int fwnode_pcs_add_provider(struct fwnode_handle *fwnode, ++ struct phylink_pcs *(*get)(struct fwnode_reference_args *pcsspec, ++ void *data), ++ void *data); ++ ++/** ++ * fwnode_pcs_del_provider - Removes a PCS provider ++ * @fwnode: Firmware node ++ */ ++void fwnode_pcs_del_provider(struct fwnode_handle *fwnode); ++ ++#endif /* __LINUX_PCS_PROVIDER_H */ +--- /dev/null ++++ b/include/linux/pcs/pcs.h +@@ -0,0 +1,56 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#ifndef __LINUX_PCS_H ++#define __LINUX_PCS_H ++ ++#include ++ ++#if IS_ENABLED(CONFIG_FWNODE_PCS) ++/** ++ * fwnode_pcs_get - Retrieves a PCS from a firmware node ++ * @fwnode: firmware node ++ * @index: index fwnode PCS handle in firmware node ++ * ++ * Get a PCS from the firmware node at index. ++ * ++ * Returns a pointer to the phylink_pcs or a negative ++ * error pointer. Can return -EPROBE_DEFER if the PCS is not ++ * present in global providers list (either due to driver ++ * still needs to be probed or it failed to probe/removed) ++ */ ++struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index); ++ ++/** ++ * fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider ++ * @fwnode: firmware node ++ * @available_pcs: pointer to preallocated array of PCS ++ * @num_pcs: where to store count of parsed PCS ++ * ++ * Generic helper function to fill available_pcs array with PCS parsed ++ * from a "pcs-handle" fwnode property defined in firmware node up to ++ * passed num_pcs. ++ * ++ * If available_pcs is NULL, num_pcs is updated with the count of the ++ * parsed PCS. ++ * ++ * Returns 0 or a negative error. ++ */ ++int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs); ++#else ++static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, ++ int index) ++{ ++ return ERR_PTR(-ENOENT); ++} ++ ++static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, ++ struct phylink_pcs **available_pcs, ++ unsigned int *num_pcs) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif /* __LINUX_PCS_H */ diff --git a/target/linux/generic/pending-6.12/770-05-net-phylink-support-late-PCS-provider-attach.patch b/target/linux/generic/pending-6.12/770-05-net-phylink-support-late-PCS-provider-attach.patch new file mode 100644 index 0000000000..d54779c4b4 --- /dev/null +++ b/target/linux/generic/pending-6.12/770-05-net-phylink-support-late-PCS-provider-attach.patch @@ -0,0 +1,254 @@ +From 684e49a015f2c5ae95ba968bb21ffc8fc36a2c7f Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 6 Apr 2025 03:28:22 +0200 +Subject: [PATCH 5/7] net: phylink: support late PCS provider attach + +Add support in phylink for late PCS provider attach to a phylink +instance. This works by creating a global notifier for the PCS provider +and making each phylink instance that makes use of fwnode subscribe to +this notifier. + +The PCS notifier will emit the event FWNODE_PCS_PROVIDER_ADD every time +a new PCS provider is added. + +phylink will then react to this event and will call the new function +fwnode_phylink_pcs_get_from_fwnode() that will check if the PCS fwnode +provided by the event is present in the phy-handle property of the +phylink instance. + +If a related PCS is found, then such PCS is added to the phylink +instance PCS list. + +Then we link the PCS to the phylink instance if it's not disable and we +refresh the supported interfaces of the phylink instance. + +Finally we check if we are in a major_config_failed scenario and trigger +an interface reconfiguration in the next phylink resolve. + +If link was previously torn down due to removal of PCS, the link will be +established again as the PCS came back and is not available to phylink. + +Signed-off-by: Christian Marangi +--- + drivers/net/pcs/pcs.c | 34 +++++++++++++++++++++++++ + drivers/net/phy/phylink.c | 52 +++++++++++++++++++++++++++++++++++++++ + include/linux/pcs/pcs.h | 48 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 134 insertions(+) + +--- a/drivers/net/pcs/pcs.c ++++ b/drivers/net/pcs/pcs.c +@@ -22,6 +22,13 @@ struct fwnode_pcs_provider { + + static LIST_HEAD(fwnode_pcs_providers); + static DEFINE_MUTEX(fwnode_pcs_mutex); ++static BLOCKING_NOTIFIER_HEAD(fwnode_pcs_notify_list); ++ ++int register_fwnode_pcs_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&fwnode_pcs_notify_list, nb); ++} ++EXPORT_SYMBOL_GPL(register_fwnode_pcs_notifier); + + struct phylink_pcs *fwnode_pcs_simple_get(struct fwnode_reference_args *pcsspec, + void *data) +@@ -55,6 +62,10 @@ int fwnode_pcs_add_provider(struct fwnod + + fwnode_dev_initialized(fwnode, true); + ++ blocking_notifier_call_chain(&fwnode_pcs_notify_list, ++ FWNODE_PCS_PROVIDER_ADD, ++ fwnode); ++ + return 0; + } + EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider); +@@ -147,6 +158,29 @@ struct phylink_pcs *fwnode_pcs_get(struc + } + EXPORT_SYMBOL_GPL(fwnode_pcs_get); + ++struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode) ++{ ++ struct fwnode_reference_args pcsspec; ++ int i = 0; ++ int ret; ++ ++ while (true) { ++ ret = fwnode_parse_pcsspec(fwnode, i, NULL, &pcsspec); ++ if (ret) ++ break; ++ ++ if (pcsspec.fwnode == pcs_fwnode) ++ break; ++ ++ i++; ++ } ++ ++ return fwnode_pcs_get(fwnode, i); ++} ++EXPORT_SYMBOL_GPL(fwnode_phylink_pcs_get_from_fwnode); ++ + static int fwnode_phylink_pcs_count(struct fwnode_handle *fwnode, + unsigned int *num_pcs) + { +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -67,6 +68,7 @@ struct phylink { + + /* List of available PCS */ + struct list_head pcs_list; ++ struct notifier_block fwnode_pcs_nb; + + /* What interface are supported by the current link. + * Can change on removal or addition of new PCS. +@@ -2039,6 +2041,51 @@ int phylink_set_fixed_link(struct phylin + } + EXPORT_SYMBOL_GPL(phylink_set_fixed_link); + ++static int pcs_provider_notify(struct notifier_block *self, ++ unsigned long val, void *data) ++{ ++ struct phylink *pl = container_of(self, struct phylink, fwnode_pcs_nb); ++ struct fwnode_handle *pcs_fwnode = data; ++ struct phylink_pcs *pcs; ++ ++ /* Check if the just added PCS provider is ++ * in the phylink instance phy-handle property ++ */ ++ pcs = fwnode_phylink_pcs_get_from_fwnode(dev_fwnode(pl->config->dev), ++ pcs_fwnode); ++ if (IS_ERR(pcs)) ++ return NOTIFY_DONE; ++ ++ /* Add the PCS */ ++ rtnl_lock(); ++ ++ list_add(&pcs->list, &pl->pcs_list); ++ ++ /* Link phylink if we are started */ ++ if (!pl->phylink_disable_state) ++ pcs->phylink = pl; ++ ++ /* Refresh supported interfaces */ ++ phy_interface_copy(pl->supported_interfaces, ++ pl->config->supported_interfaces); ++ list_for_each_entry(pcs, &pl->pcs_list, list) ++ phy_interface_or(pl->supported_interfaces, ++ pl->supported_interfaces, ++ pcs->supported_interfaces); ++ ++ mutex_lock(&pl->state_mutex); ++ /* Force an interface reconfig if major config fail */ ++ if (pl->major_config_failed) ++ pl->reconfig_interface = true; ++ mutex_unlock(&pl->state_mutex); ++ ++ rtnl_unlock(); ++ ++ phylink_run_resolve(pl); ++ ++ return NOTIFY_OK; ++} ++ + /** + * phylink_create() - create a phylink instance + * @config: a pointer to the target &struct phylink_config +@@ -2099,6 +2146,11 @@ struct phylink *phylink_create(struct ph + pl->supported_interfaces, + pcs->supported_interfaces); + ++ if (!phy_interface_empty(config->pcs_interfaces)) { ++ pl->fwnode_pcs_nb.notifier_call = pcs_provider_notify; ++ register_fwnode_pcs_notifier(&pl->fwnode_pcs_nb); ++ } ++ + pl->config = config; + if (config->type == PHYLINK_NETDEV) { + pl->netdev = to_net_dev(config->dev); +--- a/include/linux/pcs/pcs.h ++++ b/include/linux/pcs/pcs.h +@@ -4,8 +4,25 @@ + + #include + ++enum fwnode_pcs_notify_event { ++ FWNODE_PCS_PROVIDER_ADD, ++}; ++ + #if IS_ENABLED(CONFIG_FWNODE_PCS) + /** ++ * register_fwnode_pcs_notifier - Register a notifier block for fwnode ++ * PCS events ++ * @nb: pointer to the notifier block ++ * ++ * Registers a notifier block to the fwnode_pcs_notify_list blocking ++ * notifier chain. This allows phylink instance to subscribe for ++ * PCS provider events. ++ * ++ * Returns 0 or a negative error. ++ */ ++int register_fwnode_pcs_notifier(struct notifier_block *nb); ++ ++/** + * fwnode_pcs_get - Retrieves a PCS from a firmware node + * @fwnode: firmware node + * @index: index fwnode PCS handle in firmware node +@@ -21,6 +38,25 @@ struct phylink_pcs *fwnode_pcs_get(struc + int index); + + /** ++ * fwnode_phylink_pcs_get_from_fwnode - Retrieves the PCS provided ++ * by the firmware node from a ++ * firmware node ++ * @fwnode: firmware node ++ * @pcs_fwnode: PCS firmware node ++ * ++ * Parse 'pcs-handle' in 'fwnode' and get the PCS that match ++ * 'pcs_fwnode' firmware node. ++ * ++ * Returns a pointer to the phylink_pcs or a negative ++ * error pointer. Can return -EPROBE_DEFER if the PCS is not ++ * present in global providers list (either due to driver ++ * still needs to be probed or it failed to probe/removed) ++ */ ++struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode); ++ ++/** + * fwnode_phylink_pcs_parse - generic PCS parse for fwnode PCS provider + * @fwnode: firmware node + * @available_pcs: pointer to preallocated array of PCS +@@ -39,11 +75,23 @@ int fwnode_phylink_pcs_parse(struct fwno + struct phylink_pcs **available_pcs, + unsigned int *num_pcs); + #else ++static inline int register_fwnode_pcs_notifier(struct notifier_block *nb) ++{ ++ return -EOPNOTSUPP; ++} ++ + static inline struct phylink_pcs *fwnode_pcs_get(struct fwnode_handle *fwnode, + int index) + { + return ERR_PTR(-ENOENT); + } ++ ++static inline struct phylink_pcs * ++fwnode_phylink_pcs_get_from_fwnode(struct fwnode_handle *fwnode, ++ struct fwnode_handle *pcs_fwnode) ++{ ++ return ERR_PTR(-ENOENT); ++} + + static inline int fwnode_phylink_pcs_parse(struct fwnode_handle *fwnode, + struct phylink_pcs **available_pcs, diff --git a/target/linux/generic/pending-6.12/770-06-dt-bindings-net-ethernet-controller-permit-to-define.patch b/target/linux/generic/pending-6.12/770-06-dt-bindings-net-ethernet-controller-permit-to-define.patch new file mode 100644 index 0000000000..4e4333113c --- /dev/null +++ b/target/linux/generic/pending-6.12/770-06-dt-bindings-net-ethernet-controller-permit-to-define.patch @@ -0,0 +1,29 @@ +From c5d151dccce7deb62620a7b16418c0d6d6a59720 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Mon, 17 Mar 2025 23:07:45 +0100 +Subject: [PATCH 6/7] dt-bindings: net: ethernet-controller: permit to define + multiple PCS + +Drop the limitation of a single PCS in pcs-handle property. Multiple PCS +can be defined for an ethrnet-controller node to support various PHY +interface mode type. + +It's very common for SoCs to have a dedicated PCS for SGMII mode and one +for USXGMII mode. + +Signed-off-by: Christian Marangi +--- + Documentation/devicetree/bindings/net/ethernet-controller.yaml | 2 -- + 1 file changed, 2 deletions(-) + +--- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml ++++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml +@@ -110,8 +110,6 @@ properties: + + pcs-handle: + $ref: /schemas/types.yaml#/definitions/phandle-array +- items: +- maxItems: 1 + description: + Specifies a reference to a node representing a PCS PHY device on a MDIO + bus to link with an external PHY (phy-handle) if exists. diff --git a/target/linux/airoha/patches-6.12/310-08-net-phylink-add-.pcs_link_down-PCS-OP.patch b/target/linux/generic/pending-6.12/770-07-net-phylink-add-.pcs_link_down-PCS-OP.patch similarity index 69% rename from target/linux/airoha/patches-6.12/310-08-net-phylink-add-.pcs_link_down-PCS-OP.patch rename to target/linux/generic/pending-6.12/770-07-net-phylink-add-.pcs_link_down-PCS-OP.patch index 34e8d21ebd..648aa83220 100644 --- a/target/linux/airoha/patches-6.12/310-08-net-phylink-add-.pcs_link_down-PCS-OP.patch +++ b/target/linux/generic/pending-6.12/770-07-net-phylink-add-.pcs_link_down-PCS-OP.patch @@ -1,7 +1,7 @@ -From d5fb4ad1beec53ca5d3b44d9b88598ed4ab0b34d Mon Sep 17 00:00:00 2001 +From 4b1dde131a237455e41985fdc95306cd2f1b8a0a Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 9 May 2025 16:36:22 +0200 -Subject: [PATCH 1/6] net: phylink: add .pcs_link_down PCS OP +Subject: [PATCH 7/7] net: phylink: add .pcs_link_down PCS OP Permit for PCS driver to define specific operation to torn down the link between the MAC and the PCS. @@ -22,8 +22,8 @@ Signed-off-by: Christian Marangi --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -1088,6 +1088,12 @@ static unsigned int phylink_inband_caps( - return phylink_pcs_inband_caps(pcs, interface); +@@ -1178,6 +1178,12 @@ static void phylink_pcs_link_up(struct p + pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); } +static void phylink_pcs_link_down(struct phylink_pcs *pcs) @@ -32,10 +32,10 @@ Signed-off-by: Christian Marangi + pcs->ops->pcs_link_down(pcs); +} + - static void phylink_pcs_poll_stop(struct phylink *pl) - { - if (pl->cfg_link_an_mode == MLO_AN_INBAND) -@@ -1651,6 +1657,8 @@ static void phylink_link_down(struct phy + /* Query inband for a specific interface mode, asking the MAC for the + * PCS which will be used to handle the interface mode. + */ +@@ -1817,6 +1823,8 @@ static void phylink_link_down(struct phy if (ndev) netif_carrier_off(ndev); @@ -46,19 +46,19 @@ Signed-off-by: Christian Marangi phylink_info(pl, "Link is Down\n"); --- a/include/linux/phylink.h +++ b/include/linux/phylink.h -@@ -431,6 +431,7 @@ struct phylink_pcs { +@@ -443,6 +443,7 @@ struct phylink_pcs { + * @pcs_an_restart: restart 802.3z BaseX autonegotiation. + * @pcs_link_up: program the PCS for the resolved link configuration * (where necessary). ++ * @pcs_link_down: torn down link between MAC and PCS. * @pcs_pre_init: configure PCS components necessary for MAC hardware * initialization e.g. RX clock for stmmac. -+ * @pcs_link_down: torn down link between MAC and PCS. */ - struct phylink_pcs_ops { - int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported, -@@ -453,6 +454,7 @@ struct phylink_pcs_ops { +@@ -466,6 +467,7 @@ struct phylink_pcs_ops { + void (*pcs_an_restart)(struct phylink_pcs *pcs); void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, int speed, int duplex); - int (*pcs_pre_init)(struct phylink_pcs *pcs); + void (*pcs_link_down)(struct phylink_pcs *pcs); + int (*pcs_pre_init)(struct phylink_pcs *pcs); }; - #if 0 /* For kernel-doc purposes only. */ diff --git a/target/linux/layerscape/patches-6.12/702-phy-Add-2.5G-SGMII-interface-mode.patch b/target/linux/layerscape/patches-6.12/702-phy-Add-2.5G-SGMII-interface-mode.patch index dc63c7e3dd..e44f651c25 100644 --- a/target/linux/layerscape/patches-6.12/702-phy-Add-2.5G-SGMII-interface-mode.patch +++ b/target/linux/layerscape/patches-6.12/702-phy-Add-2.5G-SGMII-interface-mode.patch @@ -36,7 +36,7 @@ Signed-off-by: Bhaskar Upadhaya case PHY_INTERFACE_MODE_QUSGMII: --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -250,6 +250,7 @@ static int phylink_interface_max_speed(p +@@ -266,6 +266,7 @@ static int phylink_interface_max_speed(p case PHY_INTERFACE_MODE_GMII: return SPEED_1000; @@ -44,7 +44,7 @@ Signed-off-by: Bhaskar Upadhaya case PHY_INTERFACE_MODE_2500BASEX: case PHY_INTERFACE_MODE_10G_QXGMII: return SPEED_2500; -@@ -564,6 +565,7 @@ static unsigned long phylink_get_capabil +@@ -580,6 +581,7 @@ static unsigned long phylink_get_capabil break; case PHY_INTERFACE_MODE_2500BASEX: