xref: /linux/drivers/net/dsa/mxl862xx/mxl862xx-phylink.c (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Phylink and PCS support for MaxLinear MxL862xx switch family
4  *
5  * Copyright (C) 2024 MaxLinear Inc.
6  * Copyright (C) 2025 John Crispin <john@phrozen.org>
7  * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
8  */
9 
10 #include <linux/bitfield.h>
11 #include <linux/mutex.h>
12 #include <linux/phylink.h>
13 #include <net/dsa.h>
14 
15 #include "mxl862xx.h"
16 #include "mxl862xx-api.h"
17 #include "mxl862xx-cmd.h"
18 #include "mxl862xx-host.h"
19 #include "mxl862xx-phylink.h"
20 
21 void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
22 			       struct phylink_config *config)
23 {
24 	struct mxl862xx_priv *priv = ds->priv;
25 
26 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
27 				   MAC_100 | MAC_1000 | MAC_2500FD;
28 
29 	switch (port) {
30 	case 1 ... 8:
31 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
32 			  config->supported_interfaces);
33 		break;
34 	case 9:
35 	case 13:
36 		/* Advertised also on old firmware lacking the XPCS API:
37 		 * there the SerDes runs in its flash-configured mode
38 		 * without host control (mac_select_pcs returns NULL),
39 		 * keeping the CPU port working.
40 		 */
41 		__set_bit(PHY_INTERFACE_MODE_SGMII, config->supported_interfaces);
42 		__set_bit(PHY_INTERFACE_MODE_1000BASEX, config->supported_interfaces);
43 		__set_bit(PHY_INTERFACE_MODE_2500BASEX, config->supported_interfaces);
44 		__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
45 		__set_bit(PHY_INTERFACE_MODE_10GKR, config->supported_interfaces);
46 		__set_bit(PHY_INTERFACE_MODE_USXGMII, config->supported_interfaces);
47 		fallthrough;
48 	case 10 ... 12:
49 	case 14 ... 16:
50 		if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84))
51 			break;
52 		__set_bit(PHY_INTERFACE_MODE_QSGMII, config->supported_interfaces);
53 		__set_bit(PHY_INTERFACE_MODE_10G_QXGMII, config->supported_interfaces);
54 
55 		break;
56 	default:
57 		break;
58 	}
59 
60 	if (port == 9 || port == 13)
61 		config->mac_capabilities |= MAC_10000FD | MAC_5000FD;
62 }
63 
64 static struct mxl862xx_pcs *pcs_to_mxl862xx_pcs(struct phylink_pcs *pcs)
65 {
66 	return container_of(pcs, struct mxl862xx_pcs, pcs);
67 }
68 
69 static int mxl862xx_xpcs_if_mode(phy_interface_t interface)
70 {
71 	switch (interface) {
72 	case PHY_INTERFACE_MODE_SGMII:
73 		return MXL862XX_XPCS_IF_SGMII;
74 	case PHY_INTERFACE_MODE_QSGMII:
75 		return MXL862XX_XPCS_IF_QSGMII;
76 	case PHY_INTERFACE_MODE_1000BASEX:
77 		return MXL862XX_XPCS_IF_1000BASEX;
78 	case PHY_INTERFACE_MODE_2500BASEX:
79 		return MXL862XX_XPCS_IF_2500BASEX;
80 	case PHY_INTERFACE_MODE_USXGMII:
81 	case PHY_INTERFACE_MODE_10G_QXGMII:
82 		return MXL862XX_XPCS_IF_USXGMII;
83 	case PHY_INTERFACE_MODE_10GBASER:
84 		return MXL862XX_XPCS_IF_10GBASER;
85 	case PHY_INTERFACE_MODE_10GKR:
86 		return MXL862XX_XPCS_IF_10GKR;
87 	default:
88 		return -EINVAL;
89 	}
90 }
91 
92 static int mxl862xx_xpcs_neg_mode(unsigned int neg_mode)
93 {
94 	if (!(neg_mode & PHYLINK_PCS_NEG_INBAND))
95 		return MXL862XX_XPCS_NEG_NONE;
96 	if (neg_mode & PHYLINK_PCS_NEG_ENABLED)
97 		return MXL862XX_XPCS_NEG_INBAND_AN_ON;
98 	return MXL862XX_XPCS_NEG_INBAND_AN_OFF;
99 }
100 
101 static int mxl862xx_pcs_enable(struct phylink_pcs *pcs)
102 {
103 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
104 
105 	/* Bringup is done idempotently by pcs_config; just account this
106 	 * sub-port so pcs_disable powers the shared XPCS down only after
107 	 * the last sub-port has been released.
108 	 */
109 	mutex_lock(&mpcs->priv->serdes_lock);
110 	mpcs->priv->serdes_refcount[mpcs->serdes_id]++;
111 	mutex_unlock(&mpcs->priv->serdes_lock);
112 
113 	return 0;
114 }
115 
116 static void mxl862xx_pcs_disable(struct phylink_pcs *pcs)
117 {
118 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
119 	struct mxl862xx_xpcs_pcs_disable dis = {};
120 	struct mxl862xx_priv *priv = mpcs->priv;
121 
122 	dis.port_id = mpcs->serdes_id;
123 
124 	/* The SerDes is shared across QSGMII/QUSXGMII sub-ports; only
125 	 * power it down once the last active sub-port goes away. Hold
126 	 * serdes_lock across the count and the power-down so a sibling
127 	 * sub-port enable cannot race the transition to zero.
128 	 */
129 	mutex_lock(&priv->serdes_lock);
130 	if (--priv->serdes_refcount[mpcs->serdes_id] == 0)
131 		MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_DISABLE, dis);
132 	mutex_unlock(&priv->serdes_lock);
133 }
134 
135 /* The XPCS firmware reports failures in the result field using its own
136  * libc errno values; ENOTSUP (134) in particular has no kernel errno.
137  * Translate the codes the firmware can actually return.
138  */
139 static int mxl862xx_xpcs_errno(int result)
140 {
141 	switch (result) {
142 	case -5:	/* firmware -EIO */
143 		return -EIO;
144 	case -134:	/* firmware -ENOTSUP */
145 		return -EOPNOTSUPP;
146 	default:	/* firmware -EINVAL and anything unexpected */
147 		return -EINVAL;
148 	}
149 }
150 
151 static int mxl862xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
152 			       phy_interface_t interface,
153 			       const unsigned long *advertising,
154 			       bool permit_pause_to_mac)
155 {
156 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
157 	struct mxl862xx_priv *priv = mpcs->priv;
158 	struct mxl862xx_xpcs_pcs_cfg cfg = {};
159 	int if_mode, lane, ret, adv;
160 
161 	if_mode = mxl862xx_xpcs_if_mode(interface);
162 	if (if_mode < 0) {
163 		dev_err(priv->ds->dev, "unsupported interface: %s\n",
164 			phy_modes(interface));
165 		return if_mode;
166 	}
167 
168 	/* The XPCS bringup is per-instance and idempotent in the
169 	 * firmware: every QSGMII/QUSXGMII sub-port may call pcs_config
170 	 * and the firmware will skip the bringup if the requested mode
171 	 * matches the cached one, then update MAC pause for the
172 	 * sub-port indicated by @usx_subport. No serdes_lock is needed
173 	 * here: the refcount held since pcs_enable keeps a sibling
174 	 * pcs_disable from powering the XPCS down, and pcs_disable
175 	 * invalidates the firmware's cached mode so the next pcs_config
176 	 * redoes the bringup.
177 	 */
178 	lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
179 	       MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
180 
181 	cfg.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_CFG_PORT_ID,
182 					  mpcs->serdes_id) |
183 			       FIELD_PREP(MXL862XX_XPCS_CFG_USX_SUBPORT,
184 					  mpcs->slot) |
185 			       FIELD_PREP(MXL862XX_XPCS_CFG_USX_LANE_MODE, lane) |
186 			       FIELD_PREP(MXL862XX_XPCS_CFG_INTERFACE, if_mode) |
187 			       FIELD_PREP(MXL862XX_XPCS_CFG_NEG_MODE,
188 					  mxl862xx_xpcs_neg_mode(neg_mode)) |
189 			       FIELD_PREP(MXL862XX_XPCS_CFG_ROLE,
190 					  MXL862XX_XPCS_ROLE_MAC) |
191 			       FIELD_PREP(MXL862XX_XPCS_CFG_PERMIT_PAUSE,
192 					  permit_pause_to_mac));
193 
194 	if (neg_mode & PHYLINK_PCS_NEG_INBAND) {
195 		adv = phylink_mii_c22_pcs_encode_advertisement(interface,
196 							       advertising);
197 		if (adv >= 0)
198 			cfg.advertising.cl37 = cpu_to_le16(adv);
199 	}
200 
201 	ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_CONFIG, cfg);
202 	if (ret)
203 		return ret;
204 
205 	ret = (s16)le16_to_cpu(cfg.result);
206 	if (ret < 0)
207 		return mxl862xx_xpcs_errno(ret);
208 
209 	mpcs->interface = interface;
210 	return ret > 0 ? 1 : 0;
211 }
212 
213 static void mxl862xx_pcs_get_state(struct phylink_pcs *pcs,
214 				   unsigned int neg_mode,
215 				   struct phylink_link_state *state)
216 {
217 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
218 	struct mxl862xx_priv *priv = mpcs->priv;
219 	struct mxl862xx_xpcs_pcs_state st = {};
220 	int if_mode, lane, ret;
221 	u32 mode;
222 	u16 bmsr;
223 
224 	if_mode = mxl862xx_xpcs_if_mode(state->interface);
225 	if (if_mode < 0) {
226 		state->link = false;
227 		return;
228 	}
229 
230 	lane = (state->interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
231 	       MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
232 
233 	st.mode = cpu_to_le32(FIELD_PREP(MXL862XX_XPCS_ST_PORT_ID,
234 					 mpcs->serdes_id) |
235 			      FIELD_PREP(MXL862XX_XPCS_ST_INTERFACE, if_mode) |
236 			      FIELD_PREP(MXL862XX_XPCS_ST_USX_SUBPORT,
237 					 mpcs->slot) |
238 			      FIELD_PREP(MXL862XX_XPCS_ST_USX_LANE_MODE, lane));
239 
240 	ret = MXL862XX_API_READ(priv, MXL862XX_XPCS_PCS_GET_STATE, st);
241 	if (ret) {
242 		state->link = false;
243 		return;
244 	}
245 
246 	mode = le32_to_cpu(st.mode);
247 	state->link = FIELD_GET(MXL862XX_XPCS_ST_LINK, mode) &&
248 		      !FIELD_GET(MXL862XX_XPCS_ST_PCS_FAULT, mode);
249 	state->an_complete = FIELD_GET(MXL862XX_XPCS_ST_AN_COMPLETE, mode);
250 
251 	switch (state->interface) {
252 	case PHY_INTERFACE_MODE_1000BASEX:
253 	case PHY_INTERFACE_MODE_2500BASEX:
254 	case PHY_INTERFACE_MODE_SGMII:
255 	case PHY_INTERFACE_MODE_QSGMII:
256 		bmsr = (state->link ? BMSR_LSTATUS : 0) |
257 		       (state->an_complete ? BMSR_ANEGCOMPLETE : 0);
258 		phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr,
259 						 le16_to_cpu(st.lpa.cl37));
260 		break;
261 
262 	case PHY_INTERFACE_MODE_USXGMII:
263 	case PHY_INTERFACE_MODE_10G_QXGMII:
264 		if (state->link)
265 			phylink_decode_usxgmii_word(state,
266 						    le16_to_cpu(st.lpa.usx));
267 		break;
268 
269 	case PHY_INTERFACE_MODE_10GBASER:
270 	case PHY_INTERFACE_MODE_10GKR:
271 		if (state->link) {
272 			state->speed = SPEED_10000;
273 			state->duplex = DUPLEX_FULL;
274 		}
275 		break;
276 
277 	default:
278 		state->link = false;
279 		break;
280 	}
281 }
282 
283 static void mxl862xx_pcs_an_restart(struct phylink_pcs *pcs)
284 {
285 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
286 	struct mxl862xx_priv *priv = mpcs->priv;
287 	struct mxl862xx_xpcs_an_restart an = {};
288 	int if_mode, lane;
289 
290 	if_mode = mxl862xx_xpcs_if_mode(mpcs->interface);
291 	if (if_mode < 0)
292 		return;
293 
294 	lane = (mpcs->interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
295 	       MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
296 
297 	an.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_ANR_PORT_ID,
298 					 mpcs->serdes_id) |
299 			      FIELD_PREP(MXL862XX_XPCS_ANR_INTERFACE, if_mode) |
300 			      FIELD_PREP(MXL862XX_XPCS_ANR_USX_SUBPORT,
301 					 mpcs->slot) |
302 			      FIELD_PREP(MXL862XX_XPCS_ANR_USX_LANE_MODE, lane));
303 
304 	MXL862XX_API_WRITE(priv, MXL862XX_XPCS_AN_RESTART, an);
305 }
306 
307 static void mxl862xx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
308 				 phy_interface_t interface, int speed,
309 				 int duplex)
310 {
311 	struct mxl862xx_pcs *mpcs = pcs_to_mxl862xx_pcs(pcs);
312 	struct mxl862xx_xpcs_pcs_link_up lu = {};
313 	struct mxl862xx_priv *priv = mpcs->priv;
314 	int if_mode, lane, dup;
315 
316 	/* With inband-AN enabled (role=MAC), the XPCS auto-resolves
317 	 * speed/duplex from the partner's AN word and the firmware
318 	 * short-circuits link_up. Skip the firmware round-trip, same
319 	 * as pcs-mtk-lynxi.
320 	 */
321 	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
322 		return;
323 
324 	if_mode = mxl862xx_xpcs_if_mode(interface);
325 	if (if_mode < 0)
326 		return;
327 
328 	lane = (interface == PHY_INTERFACE_MODE_10G_QXGMII) ?
329 	       MXL862XX_XPCS_USX_QUAD : MXL862XX_XPCS_USX_SINGLE;
330 	dup = (duplex == DUPLEX_FULL) ? MXL862XX_XPCS_DUPLEX_FULL :
331 					MXL862XX_XPCS_DUPLEX_HALF;
332 
333 	lu.mode = cpu_to_le16(FIELD_PREP(MXL862XX_XPCS_LU_PORT_ID,
334 					 mpcs->serdes_id) |
335 			      FIELD_PREP(MXL862XX_XPCS_LU_INTERFACE, if_mode) |
336 			      FIELD_PREP(MXL862XX_XPCS_LU_USX_SUBPORT,
337 					 mpcs->slot) |
338 			      FIELD_PREP(MXL862XX_XPCS_LU_USX_LANE_MODE, lane) |
339 			      FIELD_PREP(MXL862XX_XPCS_LU_DUPLEX, dup));
340 	lu.speed = cpu_to_le16(speed);
341 
342 	MXL862XX_API_WRITE(priv, MXL862XX_XPCS_PCS_LINK_UP, lu);
343 }
344 
345 static unsigned int mxl862xx_pcs_inband_caps(struct phylink_pcs *pcs,
346 					     phy_interface_t interface)
347 {
348 	switch (interface) {
349 	case PHY_INTERFACE_MODE_SGMII:
350 	case PHY_INTERFACE_MODE_QSGMII:
351 	case PHY_INTERFACE_MODE_1000BASEX:
352 	case PHY_INTERFACE_MODE_2500BASEX:
353 		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
354 	case PHY_INTERFACE_MODE_USXGMII:
355 	case PHY_INTERFACE_MODE_10G_QXGMII:
356 	case PHY_INTERFACE_MODE_10GKR:
357 		return LINK_INBAND_ENABLE;
358 	case PHY_INTERFACE_MODE_10GBASER:
359 		return LINK_INBAND_DISABLE;
360 	default:
361 		return 0;
362 	}
363 }
364 
365 static const struct phylink_pcs_ops mxl862xx_pcs_ops = {
366 	.pcs_enable = mxl862xx_pcs_enable,
367 	.pcs_disable = mxl862xx_pcs_disable,
368 	.pcs_config = mxl862xx_pcs_config,
369 	.pcs_get_state = mxl862xx_pcs_get_state,
370 	.pcs_an_restart = mxl862xx_pcs_an_restart,
371 	.pcs_link_up = mxl862xx_pcs_link_up,
372 	.pcs_inband_caps = mxl862xx_pcs_inband_caps,
373 };
374 
375 void mxl862xx_setup_pcs(struct mxl862xx_priv *priv, struct mxl862xx_pcs *pcs,
376 			int port)
377 {
378 	pcs->priv = priv;
379 	pcs->serdes_id = MXL862XX_SERDES_PORT_ID(port);
380 	pcs->slot = MXL862XX_SERDES_SLOT(port);
381 	pcs->interface = PHY_INTERFACE_MODE_NA;
382 
383 	pcs->pcs.ops = &mxl862xx_pcs_ops;
384 	pcs->pcs.poll = true;
385 
386 	__set_bit(PHY_INTERFACE_MODE_QSGMII, pcs->pcs.supported_interfaces);
387 	__set_bit(PHY_INTERFACE_MODE_10G_QXGMII, pcs->pcs.supported_interfaces);
388 	if (pcs->slot != 0)
389 		return;
390 
391 	__set_bit(PHY_INTERFACE_MODE_SGMII, pcs->pcs.supported_interfaces);
392 	__set_bit(PHY_INTERFACE_MODE_1000BASEX, pcs->pcs.supported_interfaces);
393 	__set_bit(PHY_INTERFACE_MODE_2500BASEX, pcs->pcs.supported_interfaces);
394 	__set_bit(PHY_INTERFACE_MODE_10GBASER, pcs->pcs.supported_interfaces);
395 	__set_bit(PHY_INTERFACE_MODE_10GKR, pcs->pcs.supported_interfaces);
396 	__set_bit(PHY_INTERFACE_MODE_USXGMII, pcs->pcs.supported_interfaces);
397 }
398 
399 static struct phylink_pcs *
400 mxl862xx_phylink_mac_select_pcs(struct phylink_config *config,
401 				phy_interface_t interface)
402 {
403 	struct dsa_port *dp = dsa_phylink_to_port(config);
404 	struct mxl862xx_priv *priv = dp->ds->priv;
405 	int port = dp->index;
406 
407 	switch (port) {
408 	case 9 ... 16:
409 		if (!MXL862XX_FW_VER_MIN(priv, 1, 0, 84)) {
410 			dev_warn_once(dp->ds->dev,
411 				      "SerDes PCS unsupported on old firmware.\n");
412 			return NULL;
413 		}
414 		return &priv->serdes_ports[port - 9].pcs;
415 	default:
416 		return NULL;
417 	}
418 }
419 
420 static void mxl862xx_phylink_mac_config(struct phylink_config *config,
421 					unsigned int mode,
422 					const struct phylink_link_state *state)
423 {
424 }
425 
426 static void mxl862xx_phylink_mac_link_down(struct phylink_config *config,
427 					   unsigned int mode,
428 					   phy_interface_t interface)
429 {
430 }
431 
432 static void mxl862xx_phylink_mac_link_up(struct phylink_config *config,
433 					 struct phy_device *phydev,
434 					 unsigned int mode,
435 					 phy_interface_t interface,
436 					 int speed, int duplex,
437 					 bool tx_pause, bool rx_pause)
438 {
439 }
440 
441 const struct phylink_mac_ops mxl862xx_phylink_mac_ops = {
442 	.mac_config = mxl862xx_phylink_mac_config,
443 	.mac_link_down = mxl862xx_phylink_mac_link_down,
444 	.mac_link_up = mxl862xx_phylink_mac_link_up,
445 	.mac_select_pcs = mxl862xx_phylink_mac_select_pcs,
446 };
447