cf234f806b4f646ca08ebd8e8971a5b70416cdd8
[openwrt/staging/xback.git] /
1 From 5fd0f1a02e750e2db4038dee60edea669ce5aab1 Mon Sep 17 00:00:00 2001
2 From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
3 Date: Tue, 3 Dec 2024 15:31:43 +0000
4 Subject: [PATCH 12/13] net: phylink: add negotiation of in-band capabilities
5
6 Support for in-band signalling with Serdes links is uncertain. Some
7 PHYs do not support in-band for e.g. SGMII. Some PCS do not support
8 in-band for 2500Base-X. Some PCS require in-band for Base-X protocols.
9
10 Simply using what is in DT is insufficient when we have hot-pluggable
11 PHYs e.g. in the form of SFP modules, which may not provide the
12 in-band signalling.
13
14 In order to address this, we have introduced phy_inband_caps() and
15 pcs_inband_caps() functions to allow phylink to retrieve the
16 capabilities from each end of the PCS/PHY link. This commit adds code
17 to resolve whether in-band will be used in the various scenarios that
18 we have: In-band not being used, PHY present using SGMII or Base-X,
19 PHY not present. We also deal with no capabilties provided.
20
21 Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
22 Link: https://patch.msgid.link/E1tIUsJ-006IUn-H3@rmk-PC.armlinux.org.uk
23 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
24 ---
25 drivers/net/phy/phylink.c | 154 +++++++++++++++++++++++++++++++++++---
26 1 file changed, 144 insertions(+), 10 deletions(-)
27
28 --- a/drivers/net/phy/phylink.c
29 +++ b/drivers/net/phy/phylink.c
30 @@ -75,6 +75,7 @@ struct phylink {
31
32 struct mutex state_mutex;
33 struct phylink_link_state phy_state;
34 + unsigned int phy_ib_mode;
35 struct work_struct resolve;
36 unsigned int pcs_neg_mode;
37 unsigned int pcs_state;
38 @@ -1186,10 +1187,18 @@ static void phylink_pcs_neg_mode(struct
39 phy_interface_t interface,
40 const unsigned long *advertising)
41 {
42 + unsigned int pcs_ib_caps = 0;
43 + unsigned int phy_ib_caps = 0;
44 unsigned int neg_mode, mode;
45 + enum {
46 + INBAND_CISCO_SGMII,
47 + INBAND_BASEX,
48 + } type;
49
50 mode = pl->req_link_an_mode;
51
52 + pl->phy_ib_mode = 0;
53 +
54 switch (interface) {
55 case PHY_INTERFACE_MODE_SGMII:
56 case PHY_INTERFACE_MODE_QSGMII:
57 @@ -1200,10 +1209,7 @@ static void phylink_pcs_neg_mode(struct
58 * inband communication. Note: there exist PHYs that run
59 * with SGMII but do not send the inband data.
60 */
61 - if (!phylink_autoneg_inband(mode))
62 - neg_mode = PHYLINK_PCS_NEG_OUTBAND;
63 - else
64 - neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
65 + type = INBAND_CISCO_SGMII;
66 break;
67
68 case PHY_INTERFACE_MODE_1000BASEX:
69 @@ -1214,18 +1220,139 @@ static void phylink_pcs_neg_mode(struct
70 * as well, but drivers may not support this, so may
71 * need to override this.
72 */
73 - if (!phylink_autoneg_inband(mode))
74 + type = INBAND_BASEX;
75 + break;
76 +
77 + default:
78 + pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
79 + pl->act_link_an_mode = mode;
80 + return;
81 + }
82 +
83 + if (pcs)
84 + pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);
85 +
86 + if (pl->phydev)
87 + phy_ib_caps = phy_inband_caps(pl->phydev, interface);
88 +
89 + phylink_dbg(pl, "interface %s inband modes: pcs=%02x phy=%02x\n",
90 + phy_modes(interface), pcs_ib_caps, phy_ib_caps);
91 +
92 + if (!phylink_autoneg_inband(mode)) {
93 + bool pcs_ib_only = false;
94 + bool phy_ib_only = false;
95 +
96 + if (pcs_ib_caps && pcs_ib_caps != LINK_INBAND_DISABLE) {
97 + /* PCS supports reporting in-band capabilities, and
98 + * supports more than disable mode.
99 + */
100 + if (pcs_ib_caps & LINK_INBAND_DISABLE)
101 + neg_mode = PHYLINK_PCS_NEG_OUTBAND;
102 + else if (pcs_ib_caps & LINK_INBAND_ENABLE)
103 + pcs_ib_only = true;
104 + }
105 +
106 + if (phy_ib_caps && phy_ib_caps != LINK_INBAND_DISABLE) {
107 + /* PHY supports in-band capabilities, and supports
108 + * more than disable mode.
109 + */
110 + if (phy_ib_caps & LINK_INBAND_DISABLE)
111 + pl->phy_ib_mode = LINK_INBAND_DISABLE;
112 + else if (phy_ib_caps & LINK_INBAND_BYPASS)
113 + pl->phy_ib_mode = LINK_INBAND_BYPASS;
114 + else if (phy_ib_caps & LINK_INBAND_ENABLE)
115 + phy_ib_only = true;
116 + }
117 +
118 + /* If either the PCS or PHY requires inband to be enabled,
119 + * this is an invalid configuration. Provide a diagnostic
120 + * message for this case, but don't try to force the issue.
121 + */
122 + if (pcs_ib_only || phy_ib_only)
123 + phylink_warn(pl,
124 + "firmware wants %s mode, but %s%s%s requires inband\n",
125 + phylink_an_mode_str(mode),
126 + pcs_ib_only ? "PCS" : "",
127 + pcs_ib_only && phy_ib_only ? " and " : "",
128 + phy_ib_only ? "PHY" : "");
129 +
130 + neg_mode = PHYLINK_PCS_NEG_OUTBAND;
131 + } else if (type == INBAND_CISCO_SGMII || pl->phydev) {
132 + /* For SGMII modes which are designed to be used with PHYs, or
133 + * Base-X with a PHY, we try to use in-band mode where-ever
134 + * possible. However, there are some PHYs e.g. BCM84881 which
135 + * do not support in-band.
136 + */
137 + const unsigned int inband_ok = LINK_INBAND_ENABLE |
138 + LINK_INBAND_BYPASS;
139 + const unsigned int outband_ok = LINK_INBAND_DISABLE |
140 + LINK_INBAND_BYPASS;
141 + /* PCS PHY
142 + * D E D E
143 + * 0 0 0 0 no information inband enabled
144 + * 1 0 0 0 pcs doesn't support outband
145 + * 0 1 0 0 pcs required inband enabled
146 + * 1 1 0 0 pcs optional inband enabled
147 + * 0 0 1 0 phy doesn't support outband
148 + * 1 0 1 0 pcs+phy doesn't support outband
149 + * 0 1 1 0 pcs required, phy doesn't support, invalid
150 + * 1 1 1 0 pcs optional, phy doesn't support, outband
151 + * 0 0 0 1 phy required inband enabled
152 + * 1 0 0 1 pcs doesn't support, phy required, invalid
153 + * 0 1 0 1 pcs+phy required inband enabled
154 + * 1 1 0 1 pcs optional, phy required inband enabled
155 + * 0 0 1 1 phy optional inband enabled
156 + * 1 0 1 1 pcs doesn't support, phy optional, outband
157 + * 0 1 1 1 pcs required, phy optional inband enabled
158 + * 1 1 1 1 pcs+phy optional inband enabled
159 + */
160 + if ((!pcs_ib_caps || pcs_ib_caps & inband_ok) &&
161 + (!phy_ib_caps || phy_ib_caps & inband_ok)) {
162 + /* In-band supported or unknown at both ends. Enable
163 + * in-band mode with or without bypass at the PHY.
164 + */
165 + if (phy_ib_caps & LINK_INBAND_ENABLE)
166 + pl->phy_ib_mode = LINK_INBAND_ENABLE;
167 + else if (phy_ib_caps & LINK_INBAND_BYPASS)
168 + pl->phy_ib_mode = LINK_INBAND_BYPASS;
169 +
170 + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
171 + } else if ((!pcs_ib_caps || pcs_ib_caps & outband_ok) &&
172 + (!phy_ib_caps || phy_ib_caps & outband_ok)) {
173 + /* Either in-band not supported at at least one end.
174 + * In-band bypass at the other end is possible.
175 + */
176 + if (phy_ib_caps & LINK_INBAND_DISABLE)
177 + pl->phy_ib_mode = LINK_INBAND_DISABLE;
178 + else if (phy_ib_caps & LINK_INBAND_BYPASS)
179 + pl->phy_ib_mode = LINK_INBAND_BYPASS;
180 +
181 neg_mode = PHYLINK_PCS_NEG_OUTBAND;
182 + if (pl->phydev)
183 + mode = MLO_AN_PHY;
184 + } else {
185 + /* invalid */
186 + phylink_warn(pl, "%s: incompatible in-band capabilities, trying in-band",
187 + phy_modes(interface));
188 + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
189 + }
190 + } else {
191 + /* For Base-X without a PHY */
192 + if (pcs_ib_caps == LINK_INBAND_DISABLE)
193 + /* If the PCS doesn't support inband, then inband must
194 + * be disabled.
195 + */
196 + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
197 + else if (pcs_ib_caps == LINK_INBAND_ENABLE)
198 + /* If the PCS requires inband, then inband must always
199 + * be enabled.
200 + */
201 + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
202 else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
203 advertising))
204 neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
205 else
206 neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
207 - break;
208 -
209 - default:
210 - neg_mode = PHYLINK_PCS_NEG_NONE;
211 - break;
212 }
213
214 pl->pcs_neg_mode = neg_mode;
215 @@ -1324,6 +1451,13 @@ static void phylink_major_config(struct
216 ERR_PTR(err));
217 }
218
219 + if (pl->phydev && pl->phy_ib_mode) {
220 + err = phy_config_inband(pl->phydev, pl->phy_ib_mode);
221 + if (err < 0)
222 + phylink_err(pl, "phy_config_inband: %pe\n",
223 + ERR_PTR(err));
224 + }
225 +
226 if (pl->sfp_bus) {
227 rate_kbd = phylink_interface_signal_rate(state->interface);
228 if (rate_kbd)