Skip to content

Commit f99d471

Browse files
Russell King (Oracle)kuba-moo
authored andcommitted
net: phylink: add PCS negotiation mode
PCS have to work out whether they should enable PCS negotiation by looking at the "mode" and "interface" arguments, and the Autoneg bit in the advertising mask. This leads to some complex logic, so lets pull that out into phylink and instead pass a "neg_mode" argument to the PCS configuration and link up methods, instead of the "mode" argument. In order to transition drivers, add a "neg_mode" flag to the phylink PCS structure to PCS can indicate whether they want to be passed the neg_mode or the old mode argument. Signed-off-by: Russell King (Oracle) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 84ef94d commit f99d471

File tree

2 files changed

+132
-17
lines changed

2 files changed

+132
-17
lines changed

drivers/net/phy/phylink.c

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct phylink {
7171
struct mutex state_mutex;
7272
struct phylink_link_state phy_state;
7373
struct work_struct resolve;
74+
unsigned int pcs_neg_mode;
7475

7576
bool mac_link_dropped;
7677
bool using_mac_select_pcs;
@@ -992,23 +993,23 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
992993
}
993994
}
994995

995-
static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
996+
static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
996997
const struct phylink_link_state *state,
997998
bool permit_pause_to_mac)
998999
{
9991000
if (!pcs)
10001001
return 0;
10011002

1002-
return pcs->ops->pcs_config(pcs, mode, state->interface,
1003+
return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
10031004
state->advertising, permit_pause_to_mac);
10041005
}
10051006

1006-
static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
1007+
static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
10071008
phy_interface_t interface, int speed,
10081009
int duplex)
10091010
{
10101011
if (pcs && pcs->ops->pcs_link_up)
1011-
pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
1012+
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
10121013
}
10131014

10141015
static void phylink_pcs_poll_stop(struct phylink *pl)
@@ -1058,10 +1059,15 @@ static void phylink_major_config(struct phylink *pl, bool restart,
10581059
struct phylink_pcs *pcs = NULL;
10591060
bool pcs_changed = false;
10601061
unsigned int rate_kbd;
1062+
unsigned int neg_mode;
10611063
int err;
10621064

10631065
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
10641066

1067+
pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
1068+
state->interface,
1069+
state->advertising);
1070+
10651071
if (pl->using_mac_select_pcs) {
10661072
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
10671073
if (IS_ERR(pcs)) {
@@ -1094,9 +1100,12 @@ static void phylink_major_config(struct phylink *pl, bool restart,
10941100

10951101
phylink_mac_config(pl, state);
10961102

1097-
err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
1098-
!!(pl->link_config.pause &
1099-
MLO_PAUSE_AN));
1103+
neg_mode = pl->cur_link_an_mode;
1104+
if (pl->pcs && pl->pcs->neg_mode)
1105+
neg_mode = pl->pcs_neg_mode;
1106+
1107+
err = phylink_pcs_config(pl->pcs, neg_mode, state,
1108+
!!(pl->link_config.pause & MLO_PAUSE_AN));
11001109
if (err < 0)
11011110
phylink_err(pl, "pcs_config failed: %pe\n",
11021111
ERR_PTR(err));
@@ -1131,6 +1140,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
11311140
*/
11321141
static int phylink_change_inband_advert(struct phylink *pl)
11331142
{
1143+
unsigned int neg_mode;
11341144
int ret;
11351145

11361146
if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
@@ -1149,12 +1159,20 @@ static int phylink_change_inband_advert(struct phylink *pl)
11491159
__ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
11501160
pl->link_config.pause);
11511161

1162+
/* Recompute the PCS neg mode */
1163+
pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
1164+
pl->link_config.interface,
1165+
pl->link_config.advertising);
1166+
1167+
neg_mode = pl->cur_link_an_mode;
1168+
if (pl->pcs->neg_mode)
1169+
neg_mode = pl->pcs_neg_mode;
1170+
11521171
/* Modern PCS-based method; update the advert at the PCS, and
11531172
* restart negotiation if the pcs_config() helper indicates that
11541173
* the programmed advertisement has changed.
11551174
*/
1156-
ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
1157-
&pl->link_config,
1175+
ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
11581176
!!(pl->link_config.pause & MLO_PAUSE_AN));
11591177
if (ret < 0)
11601178
return ret;
@@ -1257,6 +1275,7 @@ static void phylink_link_up(struct phylink *pl,
12571275
struct phylink_link_state link_state)
12581276
{
12591277
struct net_device *ndev = pl->netdev;
1278+
unsigned int neg_mode;
12601279
int speed, duplex;
12611280
bool rx_pause;
12621281

@@ -1287,8 +1306,12 @@ static void phylink_link_up(struct phylink *pl,
12871306

12881307
pl->cur_interface = link_state.interface;
12891308

1290-
phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
1291-
speed, duplex);
1309+
neg_mode = pl->cur_link_an_mode;
1310+
if (pl->pcs && pl->pcs->neg_mode)
1311+
neg_mode = pl->pcs_neg_mode;
1312+
1313+
phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
1314+
duplex);
12921315

12931316
pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
12941317
pl->cur_interface, speed, duplex,

include/linux/phylink.h

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ enum {
2121
MLO_AN_FIXED, /* Fixed-link mode */
2222
MLO_AN_INBAND, /* In-band protocol */
2323

24+
/* PCS "negotiation" mode.
25+
* PHYLINK_PCS_NEG_NONE - protocol has no inband capability
26+
* PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
27+
* PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
28+
* 1000base-X with autoneg off
29+
* PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
30+
* Additionally, this can be tested using bitmasks:
31+
* PHYLINK_PCS_NEG_INBAND - inband mode selected
32+
* PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
33+
*/
34+
PHYLINK_PCS_NEG_NONE = 0,
35+
PHYLINK_PCS_NEG_ENABLED = BIT(4),
36+
PHYLINK_PCS_NEG_OUTBAND = BIT(5),
37+
PHYLINK_PCS_NEG_INBAND = BIT(6),
38+
PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
39+
PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
40+
PHYLINK_PCS_NEG_ENABLED,
41+
2442
/* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
2543
* autonegotiation advertisement. They correspond to the PAUSE and
2644
* ASM_DIR bits defined by 802.3, respectively.
@@ -79,6 +97,70 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
7997
return mode == MLO_AN_INBAND;
8098
}
8199

100+
/**
101+
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
102+
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
103+
* @interface: interface mode to be used
104+
* @advertising: adertisement ethtool link mode mask
105+
*
106+
* Determines the negotiation mode to be used by the PCS, and returns
107+
* one of:
108+
* %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
109+
* %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
110+
* will be used.
111+
* %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
112+
* %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
113+
*
114+
* Note: this is for cases where the PCS itself is involved in negotiation
115+
* (e.g. Clause 37, SGMII and similar) not Clause 73.
116+
*/
117+
static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
118+
phy_interface_t interface,
119+
const unsigned long *advertising)
120+
{
121+
unsigned int neg_mode;
122+
123+
switch (interface) {
124+
case PHY_INTERFACE_MODE_SGMII:
125+
case PHY_INTERFACE_MODE_QSGMII:
126+
case PHY_INTERFACE_MODE_QUSGMII:
127+
case PHY_INTERFACE_MODE_USXGMII:
128+
/* These protocols are designed for use with a PHY which
129+
* communicates its negotiation result back to the MAC via
130+
* inband communication. Note: there exist PHYs that run
131+
* with SGMII but do not send the inband data.
132+
*/
133+
if (!phylink_autoneg_inband(mode))
134+
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
135+
else
136+
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
137+
break;
138+
139+
case PHY_INTERFACE_MODE_1000BASEX:
140+
case PHY_INTERFACE_MODE_2500BASEX:
141+
/* 1000base-X is designed for use media-side for Fibre
142+
* connections, and thus the Autoneg bit needs to be
143+
* taken into account. We also do this for 2500base-X
144+
* as well, but drivers may not support this, so may
145+
* need to override this.
146+
*/
147+
if (!phylink_autoneg_inband(mode))
148+
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
149+
else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
150+
advertising))
151+
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
152+
else
153+
neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
154+
break;
155+
156+
default:
157+
neg_mode = PHYLINK_PCS_NEG_NONE;
158+
break;
159+
}
160+
161+
return neg_mode;
162+
}
163+
82164
/**
83165
* struct phylink_link_state - link state structure
84166
* @advertising: ethtool bitmask containing advertised link modes
@@ -436,13 +518,15 @@ struct phylink_pcs_ops;
436518
/**
437519
* struct phylink_pcs - PHYLINK PCS instance
438520
* @ops: a pointer to the &struct phylink_pcs_ops structure
521+
* @neg_mode: provide PCS neg mode via "mode" argument
439522
* @poll: poll the PCS for link changes
440523
*
441524
* This structure is designed to be embedded within the PCS private data,
442525
* and will be passed between phylink and the PCS.
443526
*/
444527
struct phylink_pcs {
445528
const struct phylink_pcs_ops *ops;
529+
bool neg_mode;
446530
bool poll;
447531
};
448532

@@ -460,12 +544,12 @@ struct phylink_pcs_ops {
460544
const struct phylink_link_state *state);
461545
void (*pcs_get_state)(struct phylink_pcs *pcs,
462546
struct phylink_link_state *state);
463-
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
547+
int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
464548
phy_interface_t interface,
465549
const unsigned long *advertising,
466550
bool permit_pause_to_mac);
467551
void (*pcs_an_restart)(struct phylink_pcs *pcs);
468-
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
552+
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
469553
phy_interface_t interface, int speed, int duplex);
470554
};
471555

@@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *pcs,
508592
/**
509593
* pcs_config() - Configure the PCS mode and advertisement
510594
* @pcs: a pointer to a &struct phylink_pcs.
511-
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
595+
* @neg_mode: link negotiation mode (see below)
512596
* @interface: interface mode to be used
513597
* @advertising: adertisement ethtool link mode mask
514598
* @permit_pause_to_mac: permit forwarding pause resolution to MAC
@@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *pcs,
526610
* For 1000BASE-X, the advertisement should be programmed into the PCS.
527611
*
528612
* For most 10GBASE-R, there is no advertisement.
613+
*
614+
* The %neg_mode argument should be tested via the phylink_mode_*() family of
615+
* functions, or for PCS that set pcs->neg_mode true, should be tested
616+
* against the %PHYLINK_PCS_NEG_* definitions.
529617
*/
530-
int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
618+
int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
531619
phy_interface_t interface, const unsigned long *advertising,
532620
bool permit_pause_to_mac);
533621

@@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *pcs);
543631
/**
544632
* pcs_link_up() - program the PCS for the resolved link configuration
545633
* @pcs: a pointer to a &struct phylink_pcs.
546-
* @mode: link autonegotiation mode
634+
* @neg_mode: link negotiation mode (see below)
547635
* @interface: link &typedef phy_interface_t mode
548636
* @speed: link speed
549637
* @duplex: link duplex
@@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *pcs);
552640
* the resolved link parameters. For example, a PCS operating in SGMII
553641
* mode without in-band AN needs to be manually configured for the link
554642
* and duplex setting. Otherwise, this should be a no-op.
643+
*
644+
* The %mode argument should be tested via the phylink_mode_*() family of
645+
* functions, or for PCS that set pcs->neg_mode true, should be tested
646+
* against the %PHYLINK_PCS_NEG_* definitions.
555647
*/
556-
void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
648+
void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
557649
phy_interface_t interface, int speed, int duplex);
558650
#endif
559651

0 commit comments

Comments
 (0)