[PATCH net-next v11 06/12] net: phylink: support late PCS provider attach
From: Christian Marangi
Date: Fri Aug 07 2026 - 09:36:16 EST
Add support for late PCS provider attachment to a phylink instance.
Similar to FWNODE_PCS_PROVIDER_DEL, FWNODE_PCS_PROVIDER_ADD is added to
address the case where a PCS provider is added after a phylink instance is
created and started.
The PCS notifier will emit the event FWNODE_PCS_PROVIDER_ADD every time
a new PCS provider is added.
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 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.
In the example scenario where the link was previously torn down due to
removal of PCS, the link will be established again as the PCS came back
and is now available to phylink.
Signed-off-by: Christian Marangi <ansuelsmth@xxxxxxxxx>
---
drivers/net/pcs/pcs.c | 4 ++++
drivers/net/phy/phylink.c | 41 +++++++++++++++++++++++++++++++++------
include/linux/pcs/pcs.h | 1 +
3 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/drivers/net/pcs/pcs.c b/drivers/net/pcs/pcs.c
index f575fbed896d..79fa596c2900 100644
--- a/drivers/net/pcs/pcs.c
+++ b/drivers/net/pcs/pcs.c
@@ -69,6 +69,10 @@ fwnode_pcs_add_provider(struct fwnode_handle *fwnode,
fwnode_dev_initialized(fwnode, true);
+ blocking_notifier_call_chain(&fwnode_pcs_notify_list,
+ FWNODE_PCS_PROVIDER_ADD,
+ fwnode);
+
return pp;
}
EXPORT_SYMBOL_GPL(fwnode_pcs_add_provider);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 548e783ae2aa..69d4f6675db0 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1908,6 +1908,27 @@ int phylink_set_fixed_link(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
+static void phylink_add_pcs(struct phylink *pl, struct phylink_pcs *pcs)
+{
+ struct phylink_pcs *tmp;
+
+ /*
+ * Make sure state mutex is locked to protect concurrent
+ * access to phylink instance PCS list from
+ * initial fill_available_pcs and late PCS attach
+ */
+ lockdep_assert_held(&pl->state_mutex);
+
+ list_for_each_entry(tmp, &pl->pcs_list, list)
+ if (tmp == pcs)
+ return;
+
+ list_add_tail(&pcs->list, &pl->pcs_list);
+
+ /* Link PCS to phylink */
+ pcs->phylink = pl;
+}
+
static int phylink_fill_available_pcs(struct phylink *pl,
struct phylink_config *config)
{
@@ -1939,7 +1960,7 @@ static int phylink_fill_available_pcs(struct phylink *pl,
if (!pcs)
continue;
- list_add_tail(&pcs->list, &pl->pcs_list);
+ phylink_add_pcs(pl, pcs);
}
mutex_unlock(&pl->state_mutex);
@@ -1991,7 +2012,19 @@ static int pcs_provider_notify(struct notifier_block *self,
mutex_lock(&pl->state_mutex);
- phylink_del_pcs(pl, pcs);
+ switch (val) {
+ case FWNODE_PCS_PROVIDER_ADD:
+ phylink_add_pcs(pl, pcs);
+
+ /* Force an interface reconfig if major config fail */
+ if (pl->major_config_failed)
+ pl->force_major_config = true;
+
+ break;
+ case FWNODE_PCS_PROVIDER_DEL:
+ phylink_del_pcs(pl, pcs);
+ break;
+ }
/* Refresh supported interfaces */
phy_interface_copy(pl->supported_interfaces,
@@ -2077,10 +2110,6 @@ struct phylink *phylink_create(struct phylink_config *config,
mutex_lock(&pl->state_mutex);
- /* Link available PCS to phylink */
- list_for_each_entry(pcs, &pl->pcs_list, list)
- pcs->phylink = pl;
-
phy_interface_copy(pl->supported_interfaces,
pl->config->supported_interfaces);
diff --git a/include/linux/pcs/pcs.h b/include/linux/pcs/pcs.h
index f9dcbf58d053..5cdd121b12f7 100644
--- a/include/linux/pcs/pcs.h
+++ b/include/linux/pcs/pcs.h
@@ -5,6 +5,7 @@
#include <linux/phylink.h>
enum fwnode_pcs_notify_event {
+ FWNODE_PCS_PROVIDER_ADD,
FWNODE_PCS_PROVIDER_DEL,
};
--
2.53.0