generic: 6.12: backport PCS standalone feature
Backport pending PCS standalone feature for kernel 6.12 and all the required dependency patch. All affected patch automatically refreshed. Link: https://github.com/openwrt/openwrt/pull/23271 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
parent
f9f8531903
commit
e90f3f1f5a
@ -31,7 +31,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
|||||||
|
|
||||||
--- a/drivers/net/pcs/Kconfig
|
--- a/drivers/net/pcs/Kconfig
|
||||||
+++ b/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
|
on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in
|
||||||
pass-through mode for MII.
|
pass-through mode for MII.
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
|||||||
endmenu
|
endmenu
|
||||||
--- a/drivers/net/pcs/Makefile
|
--- a/drivers/net/pcs/Makefile
|
||||||
+++ b/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_MTK_LYNXI) += pcs-mtk-lynxi.o
|
||||||
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
|
obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
|
||||||
obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
|
obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
From 486dc391ef439d45db3f7eda2229560fd2b52a78 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||||
|
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) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
|
||||||
|
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||||
|
---
|
||||||
|
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);
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
From fbb9a9d263a68f60a16c8ba5a51d6198d67171cd Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||||
|
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 <maxime.chevallier@bootlin.com>
|
||||||
|
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/E1tTffL-007RoD-1Y@rmk-PC.armlinux.org.uk
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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;
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
From f1ae32a709e0b525d7963207eb3a4747626f4818 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||||
|
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) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/E1twkqO-0006FI-Gm@rmk-PC.armlinux.org.uk
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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)
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
From 7bf588dc62a05c1866efe098e1b188fd879aa2cf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||||
|
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 <vladimir.oltean@nxp.com>
|
||||||
|
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/20260119121954.1624535-2-vladimir.oltean@nxp.com
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
From 96969b132bf1a5b875ab84fcb41a5c4972c3be9e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||||
|
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 <vladimir.oltean@nxp.com>
|
||||||
|
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/20260119121954.1624535-3-vladimir.oltean@nxp.com
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
From 0e4d7df2f3b2e59c1bccc09ea099b7a6c2dda886 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
|
||||||
|
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 <mohd.anwar@oss.qualcomm.com>
|
||||||
|
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||||
|
Link: https://patch.msgid.link/E1vl39Q-00000006utm-229h@rmk-PC.armlinux.org.uk
|
||||||
|
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||||
|
---
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||||||
|
|
||||||
--- a/drivers/net/phy/phylink.c
|
--- a/drivers/net/phy/phylink.c
|
||||||
+++ b/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 fwnode_handle *phy_fwnode;
|
||||||
struct phy_device *phy_dev;
|
struct phy_device *phy_dev;
|
||||||
@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
|||||||
|
|
||||||
/* Fixed links and 802.3z are handled without needing a PHY */
|
/* Fixed links and 802.3z are handled without needing a PHY */
|
||||||
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
|
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)
|
if (pl->config->mac_requires_rxc)
|
||||||
flags |= PHY_F_RXC_ALWAYS_ON;
|
flags |= PHY_F_RXC_ALWAYS_ON;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,88 @@
|
|||||||
|
From c6f6cb55c3c316f6169e07eacc5ccb214116719a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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);
|
||||||
@ -0,0 +1,376 @@
|
|||||||
|
From d134e22b540226a7404cabb88c86a54857486b4f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
From 1cb4e56c3ba32ac1bce89dc9c34ef2dbc9b89ad4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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);
|
||||||
|
|
||||||
@ -0,0 +1,384 @@
|
|||||||
|
From a90c644c73bbffd400cd3839fc17ffdfc69ea1e8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <daniel@makrotopia.org>
|
||||||
|
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||||
|
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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 <linux/mutex.h>
|
||||||
|
+#include <linux/property.h>
|
||||||
|
+#include <linux/phylink.h>
|
||||||
|
+#include <linux/pcs/pcs.h>
|
||||||
|
+#include <linux/pcs/pcs-provider.h>
|
||||||
|
+
|
||||||
|
+MODULE_DESCRIPTION("PCS library");
|
||||||
|
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||||
|
+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 <linux/phylink.h>
|
||||||
|
+
|
||||||
|
+#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 */
|
||||||
@ -0,0 +1,254 @@
|
|||||||
|
From 684e49a015f2c5ae95ba968bb21ffc8fc36a2c7f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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 <linux/netdevice.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_mdio.h>
|
||||||
|
+#include <linux/pcs/pcs.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/phy_fixed.h>
|
||||||
|
#include <linux/phylink.h>
|
||||||
|
@@ -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 <linux/phylink.h>
|
||||||
|
|
||||||
|
+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,
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
From c5d151dccce7deb62620a7b16418c0d6d6a59720 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
|
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 <ansuelsmth@gmail.com>
|
||||||
|
---
|
||||||
|
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.
|
||||||
@ -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 <ansuelsmth@gmail.com>
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||||
Date: Fri, 9 May 2025 16:36:22 +0200
|
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
|
Permit for PCS driver to define specific operation to torn down the link
|
||||||
between the MAC and the PCS.
|
between the MAC and the PCS.
|
||||||
@ -22,8 +22,8 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
|||||||
|
|
||||||
--- a/drivers/net/phy/phylink.c
|
--- a/drivers/net/phy/phylink.c
|
||||||
+++ b/drivers/net/phy/phylink.c
|
+++ b/drivers/net/phy/phylink.c
|
||||||
@@ -1088,6 +1088,12 @@ static unsigned int phylink_inband_caps(
|
@@ -1178,6 +1178,12 @@ static void phylink_pcs_link_up(struct p
|
||||||
return phylink_pcs_inband_caps(pcs, interface);
|
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
|
||||||
}
|
}
|
||||||
|
|
||||||
+static void phylink_pcs_link_down(struct phylink_pcs *pcs)
|
+static void phylink_pcs_link_down(struct phylink_pcs *pcs)
|
||||||
@ -32,10 +32,10 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
|||||||
+ pcs->ops->pcs_link_down(pcs);
|
+ pcs->ops->pcs_link_down(pcs);
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
static void phylink_pcs_poll_stop(struct phylink *pl)
|
/* Query inband for a specific interface mode, asking the MAC for the
|
||||||
{
|
* PCS which will be used to handle the interface mode.
|
||||||
if (pl->cfg_link_an_mode == MLO_AN_INBAND)
|
*/
|
||||||
@@ -1651,6 +1657,8 @@ static void phylink_link_down(struct phy
|
@@ -1817,6 +1823,8 @@ static void phylink_link_down(struct phy
|
||||||
|
|
||||||
if (ndev)
|
if (ndev)
|
||||||
netif_carrier_off(ndev);
|
netif_carrier_off(ndev);
|
||||||
@ -46,19 +46,19 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
|||||||
phylink_info(pl, "Link is Down\n");
|
phylink_info(pl, "Link is Down\n");
|
||||||
--- a/include/linux/phylink.h
|
--- a/include/linux/phylink.h
|
||||||
+++ b/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).
|
* (where necessary).
|
||||||
|
+ * @pcs_link_down: torn down link between MAC and PCS.
|
||||||
* @pcs_pre_init: configure PCS components necessary for MAC hardware
|
* @pcs_pre_init: configure PCS components necessary for MAC hardware
|
||||||
* initialization e.g. RX clock for stmmac.
|
* initialization e.g. RX clock for stmmac.
|
||||||
+ * @pcs_link_down: torn down link between MAC and PCS.
|
|
||||||
*/
|
*/
|
||||||
struct phylink_pcs_ops {
|
@@ -466,6 +467,7 @@ struct phylink_pcs_ops {
|
||||||
int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
|
void (*pcs_an_restart)(struct phylink_pcs *pcs);
|
||||||
@@ -453,6 +454,7 @@ struct phylink_pcs_ops {
|
|
||||||
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
|
||||||
phy_interface_t interface, int speed, int duplex);
|
phy_interface_t interface, int speed, int duplex);
|
||||||
int (*pcs_pre_init)(struct phylink_pcs *pcs);
|
|
||||||
+ void (*pcs_link_down)(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. */
|
|
||||||
@ -36,7 +36,7 @@ Signed-off-by: Bhaskar Upadhaya <Bhaskar.Upadhaya@nxp.com>
|
|||||||
case PHY_INTERFACE_MODE_QUSGMII:
|
case PHY_INTERFACE_MODE_QUSGMII:
|
||||||
--- a/drivers/net/phy/phylink.c
|
--- a/drivers/net/phy/phylink.c
|
||||||
+++ b/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:
|
case PHY_INTERFACE_MODE_GMII:
|
||||||
return SPEED_1000;
|
return SPEED_1000;
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ Signed-off-by: Bhaskar Upadhaya <Bhaskar.Upadhaya@nxp.com>
|
|||||||
case PHY_INTERFACE_MODE_2500BASEX:
|
case PHY_INTERFACE_MODE_2500BASEX:
|
||||||
case PHY_INTERFACE_MODE_10G_QXGMII:
|
case PHY_INTERFACE_MODE_10G_QXGMII:
|
||||||
return SPEED_2500;
|
return SPEED_2500;
|
||||||
@@ -564,6 +565,7 @@ static unsigned long phylink_get_capabil
|
@@ -580,6 +581,7 @@ static unsigned long phylink_get_capabil
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PHY_INTERFACE_MODE_2500BASEX:
|
case PHY_INTERFACE_MODE_2500BASEX:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user