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