generic: 6.18: backport phylink_replay_link() API

Backport phylink_replay_link() API patch from upstream kernel. This is
mostly needed for force_major_config bool in phylink struct needed for new
standalone PCS series.

While at it also rename the current 703 patch to 703-01 as it's part of the
same series merged upstream.

All patch automatically refreshed.

Link: https://github.com/openwrt/openwrt/pull/23271
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
Christian Marangi 2026-05-09 18:17:46 +02:00
parent 0c97d8707b
commit f9f8531903
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG Key ID: AC001D09ADBFEAD7
8 changed files with 188 additions and 41 deletions

View File

@ -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
@@ -28,6 +28,7 @@ enum {
PHYLINK_DISABLE_STOPPED,
PHYLINK_DISABLE_LINK,
PHYLINK_DISABLE_MAC_WOL,
+ PHYLINK_DISABLE_REPLAY,
PCS_STATE_DOWN = 0,
PCS_STATE_STARTING,
@@ -77,6 +78,7 @@ struct phylink {
bool link_failed;
bool suspend_link_up;
+ bool force_major_config;
bool major_config_failed;
bool mac_supports_eee_ops;
bool mac_supports_eee;
@@ -1681,9 +1683,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);
@@ -1691,6 +1694,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
@@ -4273,6 +4277,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
@@ -808,4 +808,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

View File

@ -20,7 +20,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -2278,7 +2278,7 @@ int phylink_fwnode_phy_connect(struct ph
@@ -2282,7 +2282,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 <daniel@makrotopia.org>
/* Fixed links and 802.3z are handled without needing a PHY */
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
@@ -2308,6 +2308,25 @@ int phylink_fwnode_phy_connect(struct ph
@@ -2312,6 +2312,25 @@ int phylink_fwnode_phy_connect(struct ph
if (pl->config->mac_requires_rxc)
flags |= PHY_F_RXC_ALWAYS_ON;

View File

@ -20,7 +20,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -59,6 +59,11 @@ struct phylink {
@@ -60,6 +60,11 @@ struct phylink {
/* The link configuration settings */
struct phylink_link_state link_config;
@ -32,7 +32,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* The current settings */
phy_interface_t cur_interface;
@@ -623,7 +628,7 @@ static int phylink_validate_mask(struct
@@ -625,7 +630,7 @@ static int phylink_validate_mask(struct
static int phylink_validate(struct phylink *pl, unsigned long *supported,
struct phylink_link_state *state)
{
@ -41,7 +41,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
if (state->interface == PHY_INTERFACE_MODE_NA)
return phylink_validate_mask(pl, NULL, supported, state,
@@ -1853,6 +1858,9 @@ struct phylink *phylink_create(struct ph
@@ -1857,6 +1862,9 @@ struct phylink *phylink_create(struct ph
mutex_init(&pl->state_mutex);
INIT_WORK(&pl->resolve, phylink_resolve);
@ -51,7 +51,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
pl->config = config;
if (config->type == PHYLINK_NETDEV) {
pl->netdev = to_net_dev(config->dev);
@@ -2011,7 +2019,7 @@ static int phylink_validate_phy(struct p
@@ -2015,7 +2023,7 @@ static int phylink_validate_phy(struct p
* those which the host supports.
*/
phy_interface_and(interfaces, phy->possible_interfaces,
@ -60,7 +60,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
if (phy_interface_empty(interfaces)) {
phylink_err(pl, "PHY has no common interfaces\n");
@@ -2753,12 +2761,12 @@ static phy_interface_t phylink_sfp_selec
@@ -2757,12 +2765,12 @@ static phy_interface_t phylink_sfp_selec
return interface;
}
@ -75,7 +75,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
return PHY_INTERFACE_MODE_NA;
}
@@ -3686,14 +3694,14 @@ static int phylink_sfp_config_optical(st
@@ -3690,14 +3698,14 @@ static int phylink_sfp_config_optical(st
phylink_dbg(pl, "optical SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
(int)PHY_INTERFACE_MODE_MAX,
@ -92,7 +92,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
pl->sfp_interfaces);
if (phy_interface_empty(pl->sfp_interfaces)) {
phylink_err(pl, "unsupported SFP module: no common interface modes\n");
@@ -3864,7 +3872,7 @@ static int phylink_sfp_connect_phy(void
@@ -3868,7 +3876,7 @@ static int phylink_sfp_connect_phy(void
/* Set the PHY's host supported interfaces */
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,

View File

@ -62,7 +62,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -59,6 +59,9 @@ struct phylink {
@@ -60,6 +60,9 @@ struct phylink {
/* The link configuration settings */
struct phylink_link_state link_config;
@ -72,7 +72,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* What interface are supported by the current link.
* Can change on removal or addition of new PCS.
*/
@@ -149,6 +152,8 @@ static const phy_interface_t phylink_sfp
@@ -151,6 +154,8 @@ static const phy_interface_t phylink_sfp
static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
@ -81,7 +81,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/**
* phylink_set_port_modes() - set the port type modes in the ethtool mask
* @mask: ethtool link mode mask
@@ -512,22 +517,59 @@ static void phylink_validate_mask_caps(u
@@ -514,22 +519,59 @@ static void phylink_validate_mask_caps(u
linkmode_and(state->advertising, state->advertising, mask);
}
@ -143,7 +143,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* 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.
@@ -539,13 +581,10 @@ static int phylink_validate_mac_and_pcs(
@@ -541,13 +583,10 @@ static int phylink_validate_mac_and_pcs(
return -EINVAL;
}
@ -161,7 +161,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
phy_modes(state->interface));
return -EINVAL;
}
@@ -959,12 +998,22 @@ static unsigned int phylink_inband_caps(
@@ -961,12 +1000,22 @@ static unsigned int phylink_inband_caps(
phy_interface_t interface)
{
struct phylink_pcs *pcs;
@ -188,7 +188,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
return 0;
return phylink_pcs_inband_caps(pcs, interface);
@@ -1259,10 +1308,36 @@ static void phylink_major_config(struct
@@ -1261,10 +1310,36 @@ static void phylink_major_config(struct
pl->major_config_failed = true;
return;
}
@ -226,7 +226,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising);
phylink_dbg(pl, "major config, active %s/%s/%s\n",
@@ -1289,11 +1364,13 @@ static void phylink_major_config(struct
@@ -1291,11 +1366,13 @@ static void phylink_major_config(struct
if (pcs_changed) {
phylink_pcs_disable(pl->pcs);
@ -244,7 +244,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
pl->pcs = pcs;
}
@@ -1840,8 +1917,9 @@ struct phylink *phylink_create(struct ph
@@ -1844,8 +1921,9 @@ struct phylink *phylink_create(struct ph
phy_interface_t iface,
const struct phylink_mac_ops *mac_ops)
{
@ -255,7 +255,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* Validate the supplied configuration */
if (phy_interface_empty(config->supported_interfaces)) {
@@ -1857,9 +1935,21 @@ struct phylink *phylink_create(struct ph
@@ -1861,9 +1939,21 @@ struct phylink *phylink_create(struct ph
mutex_init(&pl->phydev_mutex);
mutex_init(&pl->state_mutex);
INIT_WORK(&pl->resolve, phylink_resolve);
@ -277,7 +277,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
pl->config = config;
if (config->type == PHYLINK_NETDEV) {
@@ -1938,10 +2028,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
@@ -1942,10 +2032,16 @@ EXPORT_SYMBOL_GPL(phylink_create);
*/
void phylink_destroy(struct phylink *pl)
{
@ -294,7 +294,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
cancel_work_sync(&pl->resolve);
kfree(pl);
}
@@ -2443,6 +2539,7 @@ static irqreturn_t phylink_link_handler(
@@ -2447,6 +2543,7 @@ static irqreturn_t phylink_link_handler(
*/
void phylink_start(struct phylink *pl)
{
@ -302,7 +302,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
bool poll = false;
ASSERT_RTNL();
@@ -2469,6 +2566,10 @@ void phylink_start(struct phylink *pl)
@@ -2473,6 +2570,10 @@ void phylink_start(struct phylink *pl)
pl->pcs_state = PCS_STATE_STARTED;
@ -313,7 +313,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
@@ -2513,6 +2614,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
@@ -2517,6 +2618,8 @@ EXPORT_SYMBOL_GPL(phylink_start);
*/
void phylink_stop(struct phylink *pl)
{
@ -322,7 +322,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
ASSERT_RTNL();
if (pl->sfp_bus)
@@ -2530,6 +2633,14 @@ void phylink_stop(struct phylink *pl)
@@ -2534,6 +2637,14 @@ void phylink_stop(struct phylink *pl)
pl->pcs_state = PCS_STATE_DOWN;
phylink_pcs_disable(pl->pcs);

View File

@ -29,15 +29,15 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -86,6 +86,7 @@ struct phylink {
bool link_failed;
@@ -88,6 +88,7 @@ struct phylink {
bool suspend_link_up;
bool force_major_config;
bool major_config_failed;
+ bool reconfig_interface;
bool mac_supports_eee_ops;
bool mac_supports_eee;
bool phy_enable_tx_lpi;
@@ -917,6 +918,55 @@ static void phylink_resolve_an_pause(str
@@ -919,6 +920,55 @@ static void phylink_resolve_an_pause(str
}
}
@ -93,7 +93,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
phy_interface_t interface)
{
@@ -1730,6 +1780,10 @@ static void phylink_resolve(struct work_
@@ -1732,6 +1782,10 @@ static void phylink_resolve(struct work_
if (phy)
link_state.link &= pl->phy_state.link;
@ -104,20 +104,19 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* Only update if the PHY link is up */
if (phy && pl->phy_state.link) {
/* If the interface has changed, force a link down
@@ -1763,7 +1817,8 @@ static void phylink_resolve(struct work_
if (pl->act_link_an_mode != MLO_AN_FIXED)
@@ -1766,7 +1820,7 @@ static void phylink_resolve(struct work_
phylink_apply_manual_flow(pl, &link_state);
- if (mac_config && link_state.interface != pl->link_config.interface) {
+ if ((mac_config && link_state.interface != pl->link_config.interface) ||
+ pl->reconfig_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) {
+ 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.
*/
@@ -1773,6 +1828,7 @@ static void phylink_resolve(struct work_
}
@@ -1777,6 +1831,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;
}

View File

@ -102,7 +102,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/phylink.h>
@@ -61,6 +62,7 @@ struct phylink {
@@ -62,6 +63,7 @@ struct phylink {
/* List of available PCS */
struct list_head pcs_list;
@ -110,7 +110,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* What interface are supported by the current link.
* Can change on removal or addition of new PCS.
@@ -1952,6 +1954,51 @@ int phylink_set_fixed_link(struct phylin
@@ -1955,6 +1957,51 @@ int phylink_set_fixed_link(struct phylin
}
EXPORT_SYMBOL_GPL(phylink_set_fixed_link);
@ -162,7 +162,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/**
* phylink_create() - create a phylink instance
* @config: a pointer to the target &struct phylink_config
@@ -2007,6 +2054,11 @@ struct phylink *phylink_create(struct ph
@@ -2010,6 +2057,11 @@ struct phylink *phylink_create(struct ph
pl->supported_interfaces,
pcs->supported_interfaces);

View File

@ -22,7 +22,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1031,6 +1031,12 @@ static void phylink_pcs_link_up(struct p
@@ -1033,6 +1033,12 @@ static void phylink_pcs_link_up(struct p
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
}
@ -35,7 +35,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static void phylink_pcs_disable_eee(struct phylink_pcs *pcs)
{
if (pcs && pcs->ops->pcs_disable_eee)
@@ -1723,6 +1729,8 @@ static void phylink_link_down(struct phy
@@ -1725,6 +1731,8 @@ static void phylink_link_down(struct phy
phylink_deactivate_lpi(pl);