1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. |
4 | * Synopsys DesignWare XPCS helpers |
5 | * |
6 | * Author: Jose Abreu <Jose.Abreu@synopsys.com> |
7 | */ |
8 | |
9 | #include <linux/delay.h> |
10 | #include <linux/pcs/pcs-xpcs.h> |
11 | #include <linux/mdio.h> |
12 | #include <linux/phylink.h> |
13 | |
14 | #include "pcs-xpcs.h" |
15 | |
16 | #define phylink_pcs_to_xpcs(pl_pcs) \ |
17 | container_of((pl_pcs), struct dw_xpcs, pcs) |
18 | |
19 | static const int xpcs_usxgmii_features[] = { |
20 | ETHTOOL_LINK_MODE_Pause_BIT, |
21 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
22 | ETHTOOL_LINK_MODE_Autoneg_BIT, |
23 | ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, |
24 | ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, |
25 | ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, |
26 | ETHTOOL_LINK_MODE_2500baseX_Full_BIT, |
27 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
28 | }; |
29 | |
30 | static const int xpcs_10gkr_features[] = { |
31 | ETHTOOL_LINK_MODE_Pause_BIT, |
32 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
33 | ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, |
34 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
35 | }; |
36 | |
37 | static const int xpcs_xlgmii_features[] = { |
38 | ETHTOOL_LINK_MODE_Pause_BIT, |
39 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
40 | ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, |
41 | ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, |
42 | ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, |
43 | ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, |
44 | ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, |
45 | ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, |
46 | ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, |
47 | ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, |
48 | ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, |
49 | ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, |
50 | ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, |
51 | ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, |
52 | ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, |
53 | ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, |
54 | ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, |
55 | ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, |
56 | ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, |
57 | ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, |
58 | ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, |
59 | ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, |
60 | ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, |
61 | ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, |
62 | ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, |
63 | ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, |
64 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
65 | }; |
66 | |
67 | static const int xpcs_10gbaser_features[] = { |
68 | ETHTOOL_LINK_MODE_Pause_BIT, |
69 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
70 | ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, |
71 | ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, |
72 | ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, |
73 | ETHTOOL_LINK_MODE_10000baseER_Full_BIT, |
74 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
75 | }; |
76 | |
77 | static const int xpcs_sgmii_features[] = { |
78 | ETHTOOL_LINK_MODE_Pause_BIT, |
79 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
80 | ETHTOOL_LINK_MODE_Autoneg_BIT, |
81 | ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
82 | ETHTOOL_LINK_MODE_10baseT_Full_BIT, |
83 | ETHTOOL_LINK_MODE_100baseT_Half_BIT, |
84 | ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
85 | ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
86 | ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
87 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
88 | }; |
89 | |
90 | static const int xpcs_1000basex_features[] = { |
91 | ETHTOOL_LINK_MODE_Pause_BIT, |
92 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
93 | ETHTOOL_LINK_MODE_Autoneg_BIT, |
94 | ETHTOOL_LINK_MODE_1000baseX_Full_BIT, |
95 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
96 | }; |
97 | |
98 | static const int xpcs_2500basex_features[] = { |
99 | ETHTOOL_LINK_MODE_Pause_BIT, |
100 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
101 | ETHTOOL_LINK_MODE_Autoneg_BIT, |
102 | ETHTOOL_LINK_MODE_2500baseX_Full_BIT, |
103 | ETHTOOL_LINK_MODE_2500baseT_Full_BIT, |
104 | __ETHTOOL_LINK_MODE_MASK_NBITS, |
105 | }; |
106 | |
107 | static const phy_interface_t xpcs_usxgmii_interfaces[] = { |
108 | PHY_INTERFACE_MODE_USXGMII, |
109 | }; |
110 | |
111 | static const phy_interface_t xpcs_10gkr_interfaces[] = { |
112 | PHY_INTERFACE_MODE_10GKR, |
113 | }; |
114 | |
115 | static const phy_interface_t xpcs_xlgmii_interfaces[] = { |
116 | PHY_INTERFACE_MODE_XLGMII, |
117 | }; |
118 | |
119 | static const phy_interface_t xpcs_10gbaser_interfaces[] = { |
120 | PHY_INTERFACE_MODE_10GBASER, |
121 | }; |
122 | |
123 | static const phy_interface_t xpcs_sgmii_interfaces[] = { |
124 | PHY_INTERFACE_MODE_SGMII, |
125 | }; |
126 | |
127 | static const phy_interface_t xpcs_1000basex_interfaces[] = { |
128 | PHY_INTERFACE_MODE_1000BASEX, |
129 | }; |
130 | |
131 | static const phy_interface_t xpcs_2500basex_interfaces[] = { |
132 | PHY_INTERFACE_MODE_2500BASEX, |
133 | }; |
134 | |
135 | enum { |
136 | DW_XPCS_USXGMII, |
137 | DW_XPCS_10GKR, |
138 | DW_XPCS_XLGMII, |
139 | DW_XPCS_10GBASER, |
140 | DW_XPCS_SGMII, |
141 | DW_XPCS_1000BASEX, |
142 | DW_XPCS_2500BASEX, |
143 | DW_XPCS_INTERFACE_MAX, |
144 | }; |
145 | |
146 | struct xpcs_compat { |
147 | const int *supported; |
148 | const phy_interface_t *interface; |
149 | int num_interfaces; |
150 | int an_mode; |
151 | int (*pma_config)(struct dw_xpcs *xpcs); |
152 | }; |
153 | |
154 | struct xpcs_id { |
155 | u32 id; |
156 | u32 mask; |
157 | const struct xpcs_compat *compat; |
158 | }; |
159 | |
160 | static const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id, |
161 | phy_interface_t interface) |
162 | { |
163 | int i, j; |
164 | |
165 | for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { |
166 | const struct xpcs_compat *compat = &id->compat[i]; |
167 | |
168 | for (j = 0; j < compat->num_interfaces; j++) |
169 | if (compat->interface[j] == interface) |
170 | return compat; |
171 | } |
172 | |
173 | return NULL; |
174 | } |
175 | |
176 | int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface) |
177 | { |
178 | const struct xpcs_compat *compat; |
179 | |
180 | compat = xpcs_find_compat(id: xpcs->id, interface); |
181 | if (!compat) |
182 | return -ENODEV; |
183 | |
184 | return compat->an_mode; |
185 | } |
186 | EXPORT_SYMBOL_GPL(xpcs_get_an_mode); |
187 | |
188 | static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, |
189 | enum ethtool_link_mode_bit_indices linkmode) |
190 | { |
191 | int i; |
192 | |
193 | for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) |
194 | if (compat->supported[i] == linkmode) |
195 | return true; |
196 | |
197 | return false; |
198 | } |
199 | |
200 | #define xpcs_linkmode_supported(compat, mode) \ |
201 | __xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT) |
202 | |
203 | int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg) |
204 | { |
205 | return mdiodev_c45_read(mdiodev: xpcs->mdiodev, devad: dev, regnum: reg); |
206 | } |
207 | |
208 | int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) |
209 | { |
210 | return mdiodev_c45_write(mdiodev: xpcs->mdiodev, devad: dev, regnum: reg, val); |
211 | } |
212 | |
213 | static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, |
214 | u16 mask, u16 set) |
215 | { |
216 | return mdiodev_c45_modify_changed(mdiodev: xpcs->mdiodev, devad: dev, regnum: reg, mask, set); |
217 | } |
218 | |
219 | static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg) |
220 | { |
221 | return xpcs_read(xpcs, dev, DW_VENDOR | reg); |
222 | } |
223 | |
224 | static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, |
225 | u16 val) |
226 | { |
227 | return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); |
228 | } |
229 | |
230 | int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) |
231 | { |
232 | return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); |
233 | } |
234 | |
235 | int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) |
236 | { |
237 | return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); |
238 | } |
239 | |
240 | static int xpcs_dev_flag(struct dw_xpcs *xpcs) |
241 | { |
242 | int ret, oui; |
243 | |
244 | ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID1); |
245 | if (ret < 0) |
246 | return ret; |
247 | |
248 | oui = ret; |
249 | |
250 | ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2); |
251 | if (ret < 0) |
252 | return ret; |
253 | |
254 | ret = (ret >> 10) & 0x3F; |
255 | oui |= ret << 16; |
256 | |
257 | if (oui == DW_OUI_WX) |
258 | xpcs->dev_flag = DW_DEV_TXGBE; |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev) |
264 | { |
265 | /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ |
266 | unsigned int retries = 12; |
267 | int ret; |
268 | |
269 | do { |
270 | msleep(msecs: 50); |
271 | ret = xpcs_read(xpcs, dev, MDIO_CTRL1); |
272 | if (ret < 0) |
273 | return ret; |
274 | } while (ret & MDIO_CTRL1_RESET && --retries); |
275 | |
276 | return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; |
277 | } |
278 | |
279 | static int xpcs_soft_reset(struct dw_xpcs *xpcs, |
280 | const struct xpcs_compat *compat) |
281 | { |
282 | int ret, dev; |
283 | |
284 | switch (compat->an_mode) { |
285 | case DW_AN_C73: |
286 | case DW_10GBASER: |
287 | dev = MDIO_MMD_PCS; |
288 | break; |
289 | case DW_AN_C37_SGMII: |
290 | case DW_2500BASEX: |
291 | case DW_AN_C37_1000BASEX: |
292 | dev = MDIO_MMD_VEND2; |
293 | break; |
294 | default: |
295 | return -EINVAL; |
296 | } |
297 | |
298 | ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET); |
299 | if (ret < 0) |
300 | return ret; |
301 | |
302 | return xpcs_poll_reset(xpcs, dev); |
303 | } |
304 | |
305 | #define xpcs_warn(__xpcs, __state, __args...) \ |
306 | ({ \ |
307 | if ((__state)->link) \ |
308 | dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \ |
309 | }) |
310 | |
311 | static int xpcs_read_fault_c73(struct dw_xpcs *xpcs, |
312 | struct phylink_link_state *state, |
313 | u16 pcs_stat1) |
314 | { |
315 | int ret; |
316 | |
317 | if (pcs_stat1 & MDIO_STAT1_FAULT) { |
318 | xpcs_warn(xpcs, state, "Link fault condition detected!\n" ); |
319 | return -EFAULT; |
320 | } |
321 | |
322 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2); |
323 | if (ret < 0) |
324 | return ret; |
325 | |
326 | if (ret & MDIO_STAT2_RXFAULT) |
327 | xpcs_warn(xpcs, state, "Receiver fault detected!\n" ); |
328 | if (ret & MDIO_STAT2_TXFAULT) |
329 | xpcs_warn(xpcs, state, "Transmitter fault detected!\n" ); |
330 | |
331 | ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS); |
332 | if (ret < 0) |
333 | return ret; |
334 | |
335 | if (ret & DW_RXFIFO_ERR) { |
336 | xpcs_warn(xpcs, state, "FIFO fault condition detected!\n" ); |
337 | return -EFAULT; |
338 | } |
339 | |
340 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); |
341 | if (ret < 0) |
342 | return ret; |
343 | |
344 | if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK)) |
345 | xpcs_warn(xpcs, state, "Link is not locked!\n" ); |
346 | |
347 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2); |
348 | if (ret < 0) |
349 | return ret; |
350 | |
351 | if (ret & MDIO_PCS_10GBRT_STAT2_ERR) { |
352 | xpcs_warn(xpcs, state, "Link has errors!\n" ); |
353 | return -EFAULT; |
354 | } |
355 | |
356 | return 0; |
357 | } |
358 | |
359 | static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed) |
360 | { |
361 | int ret, speed_sel; |
362 | |
363 | switch (speed) { |
364 | case SPEED_10: |
365 | speed_sel = DW_USXGMII_10; |
366 | break; |
367 | case SPEED_100: |
368 | speed_sel = DW_USXGMII_100; |
369 | break; |
370 | case SPEED_1000: |
371 | speed_sel = DW_USXGMII_1000; |
372 | break; |
373 | case SPEED_2500: |
374 | speed_sel = DW_USXGMII_2500; |
375 | break; |
376 | case SPEED_5000: |
377 | speed_sel = DW_USXGMII_5000; |
378 | break; |
379 | case SPEED_10000: |
380 | speed_sel = DW_USXGMII_10000; |
381 | break; |
382 | default: |
383 | /* Nothing to do here */ |
384 | return; |
385 | } |
386 | |
387 | ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); |
388 | if (ret < 0) |
389 | goto out; |
390 | |
391 | ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, val: ret | DW_USXGMII_EN); |
392 | if (ret < 0) |
393 | goto out; |
394 | |
395 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); |
396 | if (ret < 0) |
397 | goto out; |
398 | |
399 | ret &= ~DW_USXGMII_SS_MASK; |
400 | ret |= speed_sel | DW_USXGMII_FULL; |
401 | |
402 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val: ret); |
403 | if (ret < 0) |
404 | goto out; |
405 | |
406 | ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); |
407 | if (ret < 0) |
408 | goto out; |
409 | |
410 | ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, val: ret | DW_USXGMII_RST); |
411 | if (ret < 0) |
412 | goto out; |
413 | |
414 | return; |
415 | |
416 | out: |
417 | pr_err("%s: XPCS access returned %pe\n" , __func__, ERR_PTR(ret)); |
418 | } |
419 | |
420 | static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs, |
421 | const struct xpcs_compat *compat) |
422 | { |
423 | int ret, adv; |
424 | |
425 | /* By default, in USXGMII mode XPCS operates at 10G baud and |
426 | * replicates data to achieve lower speeds. Hereby, in this |
427 | * default configuration we need to advertise all supported |
428 | * modes and not only the ones we want to use. |
429 | */ |
430 | |
431 | /* SR_AN_ADV3 */ |
432 | adv = 0; |
433 | if (xpcs_linkmode_supported(compat, 2500baseX_Full)) |
434 | adv |= DW_C73_2500KX; |
435 | |
436 | /* TODO: 5000baseKR */ |
437 | |
438 | ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, val: adv); |
439 | if (ret < 0) |
440 | return ret; |
441 | |
442 | /* SR_AN_ADV2 */ |
443 | adv = 0; |
444 | if (xpcs_linkmode_supported(compat, 1000baseKX_Full)) |
445 | adv |= DW_C73_1000KX; |
446 | if (xpcs_linkmode_supported(compat, 10000baseKX4_Full)) |
447 | adv |= DW_C73_10000KX4; |
448 | if (xpcs_linkmode_supported(compat, 10000baseKR_Full)) |
449 | adv |= DW_C73_10000KR; |
450 | |
451 | ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, val: adv); |
452 | if (ret < 0) |
453 | return ret; |
454 | |
455 | /* SR_AN_ADV1 */ |
456 | adv = DW_C73_AN_ADV_SF; |
457 | if (xpcs_linkmode_supported(compat, Pause)) |
458 | adv |= DW_C73_PAUSE; |
459 | if (xpcs_linkmode_supported(compat, Asym_Pause)) |
460 | adv |= DW_C73_ASYM_PAUSE; |
461 | |
462 | return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, val: adv); |
463 | } |
464 | |
465 | static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs, |
466 | const struct xpcs_compat *compat) |
467 | { |
468 | int ret; |
469 | |
470 | ret = _xpcs_config_aneg_c73(xpcs, compat); |
471 | if (ret < 0) |
472 | return ret; |
473 | |
474 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); |
475 | if (ret < 0) |
476 | return ret; |
477 | |
478 | ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; |
479 | |
480 | return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, val: ret); |
481 | } |
482 | |
483 | static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs, |
484 | struct phylink_link_state *state, |
485 | const struct xpcs_compat *compat, u16 an_stat1) |
486 | { |
487 | int ret; |
488 | |
489 | if (an_stat1 & MDIO_AN_STAT1_COMPLETE) { |
490 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA); |
491 | if (ret < 0) |
492 | return ret; |
493 | |
494 | /* Check if Aneg outcome is valid */ |
495 | if (!(ret & DW_C73_AN_ADV_SF)) { |
496 | xpcs_config_aneg_c73(xpcs, compat); |
497 | return 0; |
498 | } |
499 | |
500 | return 1; |
501 | } |
502 | |
503 | return 0; |
504 | } |
505 | |
506 | static int xpcs_read_lpa_c73(struct dw_xpcs *xpcs, |
507 | struct phylink_link_state *state, u16 an_stat1) |
508 | { |
509 | u16 lpa[3]; |
510 | int i, ret; |
511 | |
512 | if (!(an_stat1 & MDIO_AN_STAT1_LPABLE)) { |
513 | phylink_clear(state->lp_advertising, Autoneg); |
514 | return 0; |
515 | } |
516 | |
517 | phylink_set(state->lp_advertising, Autoneg); |
518 | |
519 | /* Read Clause 73 link partner advertisement */ |
520 | for (i = ARRAY_SIZE(lpa); --i >= 0; ) { |
521 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_AN_LPA + i); |
522 | if (ret < 0) |
523 | return ret; |
524 | |
525 | lpa[i] = ret; |
526 | } |
527 | |
528 | mii_c73_mod_linkmode(adv: state->lp_advertising, lpa); |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs, |
534 | struct phylink_link_state *state) |
535 | { |
536 | unsigned long *adv = state->advertising; |
537 | int speed = SPEED_UNKNOWN; |
538 | int bit; |
539 | |
540 | for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) { |
541 | int new_speed = SPEED_UNKNOWN; |
542 | |
543 | switch (bit) { |
544 | case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT: |
545 | case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT: |
546 | case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT: |
547 | new_speed = SPEED_25000; |
548 | break; |
549 | case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT: |
550 | case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT: |
551 | case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT: |
552 | case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT: |
553 | new_speed = SPEED_40000; |
554 | break; |
555 | case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT: |
556 | case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT: |
557 | case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT: |
558 | case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT: |
559 | case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT: |
560 | case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT: |
561 | case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT: |
562 | case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT: |
563 | new_speed = SPEED_50000; |
564 | break; |
565 | case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT: |
566 | case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT: |
567 | case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT: |
568 | case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT: |
569 | case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT: |
570 | case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT: |
571 | case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT: |
572 | case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT: |
573 | case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT: |
574 | new_speed = SPEED_100000; |
575 | break; |
576 | default: |
577 | continue; |
578 | } |
579 | |
580 | if (new_speed > speed) |
581 | speed = new_speed; |
582 | } |
583 | |
584 | return speed; |
585 | } |
586 | |
587 | static void xpcs_resolve_pma(struct dw_xpcs *xpcs, |
588 | struct phylink_link_state *state) |
589 | { |
590 | state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; |
591 | state->duplex = DUPLEX_FULL; |
592 | |
593 | switch (state->interface) { |
594 | case PHY_INTERFACE_MODE_10GKR: |
595 | state->speed = SPEED_10000; |
596 | break; |
597 | case PHY_INTERFACE_MODE_XLGMII: |
598 | state->speed = xpcs_get_max_xlgmii_speed(xpcs, state); |
599 | break; |
600 | default: |
601 | state->speed = SPEED_UNKNOWN; |
602 | break; |
603 | } |
604 | } |
605 | |
606 | static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported, |
607 | const struct phylink_link_state *state) |
608 | { |
609 | __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, }; |
610 | const struct xpcs_compat *compat; |
611 | struct dw_xpcs *xpcs; |
612 | int i; |
613 | |
614 | xpcs = phylink_pcs_to_xpcs(pcs); |
615 | compat = xpcs_find_compat(id: xpcs->id, interface: state->interface); |
616 | if (!compat) |
617 | return -EINVAL; |
618 | |
619 | /* Populate the supported link modes for this PHY interface type. |
620 | * FIXME: what about the port modes and autoneg bit? This masks |
621 | * all those away. |
622 | */ |
623 | for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) |
624 | set_bit(nr: compat->supported[i], addr: xpcs_supported); |
625 | |
626 | linkmode_and(dst: supported, a: supported, b: xpcs_supported); |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces) |
632 | { |
633 | int i, j; |
634 | |
635 | for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { |
636 | const struct xpcs_compat *compat = &xpcs->id->compat[i]; |
637 | |
638 | for (j = 0; j < compat->num_interfaces; j++) |
639 | __set_bit(compat->interface[j], interfaces); |
640 | } |
641 | } |
642 | EXPORT_SYMBOL_GPL(xpcs_get_interfaces); |
643 | |
644 | int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable) |
645 | { |
646 | int ret; |
647 | |
648 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0); |
649 | if (ret < 0) |
650 | return ret; |
651 | |
652 | if (enable) { |
653 | /* Enable EEE */ |
654 | ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | |
655 | DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | |
656 | DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | |
657 | mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT; |
658 | } else { |
659 | ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | |
660 | DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | |
661 | DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | |
662 | DW_VR_MII_EEE_MULT_FACT_100NS); |
663 | } |
664 | |
665 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, val: ret); |
666 | if (ret < 0) |
667 | return ret; |
668 | |
669 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1); |
670 | if (ret < 0) |
671 | return ret; |
672 | |
673 | if (enable) |
674 | ret |= DW_VR_MII_EEE_TRN_LPI; |
675 | else |
676 | ret &= ~DW_VR_MII_EEE_TRN_LPI; |
677 | |
678 | return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, val: ret); |
679 | } |
680 | EXPORT_SYMBOL_GPL(xpcs_config_eee); |
681 | |
682 | static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, |
683 | unsigned int neg_mode) |
684 | { |
685 | int ret, mdio_ctrl, tx_conf; |
686 | |
687 | if (xpcs->dev_flag == DW_DEV_TXGBE) |
688 | xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); |
689 | |
690 | /* For AN for C37 SGMII mode, the settings are :- |
691 | * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case |
692 | it is already enabled) |
693 | * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) |
694 | * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) |
695 | * DW xPCS used with DW EQoS MAC is always MAC side SGMII. |
696 | * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic |
697 | * speed/duplex mode change by HW after SGMII AN complete) |
698 | * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN) |
699 | * |
700 | * Note: Since it is MAC side SGMII, there is no need to set |
701 | * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from |
702 | * PHY about the link state change after C28 AN is completed |
703 | * between PHY and Link Partner. There is also no need to |
704 | * trigger AN restart for MAC-side SGMII. |
705 | */ |
706 | mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); |
707 | if (mdio_ctrl < 0) |
708 | return mdio_ctrl; |
709 | |
710 | if (mdio_ctrl & AN_CL37_EN) { |
711 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, |
712 | val: mdio_ctrl & ~AN_CL37_EN); |
713 | if (ret < 0) |
714 | return ret; |
715 | } |
716 | |
717 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); |
718 | if (ret < 0) |
719 | return ret; |
720 | |
721 | ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); |
722 | ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << |
723 | DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & |
724 | DW_VR_MII_PCS_MODE_MASK); |
725 | if (xpcs->dev_flag == DW_DEV_TXGBE) { |
726 | ret |= DW_VR_MII_AN_CTRL_8BIT; |
727 | /* Hardware requires it to be PHY side SGMII */ |
728 | tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII; |
729 | } else { |
730 | tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII; |
731 | } |
732 | ret |= tx_conf << DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & |
733 | DW_VR_MII_TX_CONFIG_MASK; |
734 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, val: ret); |
735 | if (ret < 0) |
736 | return ret; |
737 | |
738 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); |
739 | if (ret < 0) |
740 | return ret; |
741 | |
742 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) |
743 | ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; |
744 | else |
745 | ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; |
746 | |
747 | if (xpcs->dev_flag == DW_DEV_TXGBE) |
748 | ret |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL; |
749 | |
750 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, val: ret); |
751 | if (ret < 0) |
752 | return ret; |
753 | |
754 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) |
755 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, |
756 | val: mdio_ctrl | AN_CL37_EN); |
757 | |
758 | return ret; |
759 | } |
760 | |
761 | static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, |
762 | unsigned int neg_mode, |
763 | const unsigned long *advertising) |
764 | { |
765 | phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX; |
766 | int ret, mdio_ctrl, adv; |
767 | bool changed = 0; |
768 | |
769 | if (xpcs->dev_flag == DW_DEV_TXGBE) |
770 | xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_CL37_BP | DW_EN_VSMMD1); |
771 | |
772 | /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must |
773 | * be disabled first:- |
774 | * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b |
775 | * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37) |
776 | */ |
777 | mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); |
778 | if (mdio_ctrl < 0) |
779 | return mdio_ctrl; |
780 | |
781 | if (mdio_ctrl & AN_CL37_EN) { |
782 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, |
783 | val: mdio_ctrl & ~AN_CL37_EN); |
784 | if (ret < 0) |
785 | return ret; |
786 | } |
787 | |
788 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); |
789 | if (ret < 0) |
790 | return ret; |
791 | |
792 | ret &= ~DW_VR_MII_PCS_MODE_MASK; |
793 | if (!xpcs->pcs.poll) |
794 | ret |= DW_VR_MII_AN_INTR_EN; |
795 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, val: ret); |
796 | if (ret < 0) |
797 | return ret; |
798 | |
799 | /* Check for advertising changes and update the C45 MII ADV |
800 | * register accordingly. |
801 | */ |
802 | adv = phylink_mii_c22_pcs_encode_advertisement(interface, |
803 | advertising); |
804 | if (adv >= 0) { |
805 | ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2, |
806 | MII_ADVERTISE, mask: 0xffff, set: adv); |
807 | if (ret < 0) |
808 | return ret; |
809 | |
810 | changed = ret; |
811 | } |
812 | |
813 | /* Clear CL37 AN complete status */ |
814 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, val: 0); |
815 | if (ret < 0) |
816 | return ret; |
817 | |
818 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { |
819 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, |
820 | val: mdio_ctrl | AN_CL37_EN); |
821 | if (ret < 0) |
822 | return ret; |
823 | } |
824 | |
825 | return changed; |
826 | } |
827 | |
828 | static int xpcs_config_2500basex(struct dw_xpcs *xpcs) |
829 | { |
830 | int ret; |
831 | |
832 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); |
833 | if (ret < 0) |
834 | return ret; |
835 | ret |= DW_VR_MII_DIG_CTRL1_2G5_EN; |
836 | ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; |
837 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, val: ret); |
838 | if (ret < 0) |
839 | return ret; |
840 | |
841 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); |
842 | if (ret < 0) |
843 | return ret; |
844 | ret &= ~AN_CL37_EN; |
845 | ret |= SGMII_SPEED_SS6; |
846 | ret &= ~SGMII_SPEED_SS13; |
847 | return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, val: ret); |
848 | } |
849 | |
850 | int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, |
851 | const unsigned long *advertising, unsigned int neg_mode) |
852 | { |
853 | const struct xpcs_compat *compat; |
854 | int ret; |
855 | |
856 | compat = xpcs_find_compat(id: xpcs->id, interface); |
857 | if (!compat) |
858 | return -ENODEV; |
859 | |
860 | if (xpcs->dev_flag == DW_DEV_TXGBE) { |
861 | ret = txgbe_xpcs_switch_mode(xpcs, interface); |
862 | if (ret) |
863 | return ret; |
864 | } |
865 | |
866 | switch (compat->an_mode) { |
867 | case DW_10GBASER: |
868 | break; |
869 | case DW_AN_C73: |
870 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { |
871 | ret = xpcs_config_aneg_c73(xpcs, compat); |
872 | if (ret) |
873 | return ret; |
874 | } |
875 | break; |
876 | case DW_AN_C37_SGMII: |
877 | ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode); |
878 | if (ret) |
879 | return ret; |
880 | break; |
881 | case DW_AN_C37_1000BASEX: |
882 | ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode, |
883 | advertising); |
884 | if (ret) |
885 | return ret; |
886 | break; |
887 | case DW_2500BASEX: |
888 | ret = xpcs_config_2500basex(xpcs); |
889 | if (ret) |
890 | return ret; |
891 | break; |
892 | default: |
893 | return -EINVAL; |
894 | } |
895 | |
896 | if (compat->pma_config) { |
897 | ret = compat->pma_config(xpcs); |
898 | if (ret) |
899 | return ret; |
900 | } |
901 | |
902 | return 0; |
903 | } |
904 | EXPORT_SYMBOL_GPL(xpcs_do_config); |
905 | |
906 | static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, |
907 | phy_interface_t interface, |
908 | const unsigned long *advertising, |
909 | bool permit_pause_to_mac) |
910 | { |
911 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
912 | |
913 | return xpcs_do_config(xpcs, interface, advertising, neg_mode); |
914 | } |
915 | |
916 | static int xpcs_get_state_c73(struct dw_xpcs *xpcs, |
917 | struct phylink_link_state *state, |
918 | const struct xpcs_compat *compat) |
919 | { |
920 | bool an_enabled; |
921 | int pcs_stat1; |
922 | int an_stat1; |
923 | int ret; |
924 | |
925 | /* The link status bit is latching-low, so it is important to |
926 | * avoid unnecessary re-reads of this register to avoid missing |
927 | * a link-down event. |
928 | */ |
929 | pcs_stat1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); |
930 | if (pcs_stat1 < 0) { |
931 | state->link = false; |
932 | return pcs_stat1; |
933 | } |
934 | |
935 | /* Link needs to be read first ... */ |
936 | state->link = !!(pcs_stat1 & MDIO_STAT1_LSTATUS); |
937 | |
938 | /* ... and then we check the faults. */ |
939 | ret = xpcs_read_fault_c73(xpcs, state, pcs_stat1); |
940 | if (ret) { |
941 | ret = xpcs_soft_reset(xpcs, compat); |
942 | if (ret) |
943 | return ret; |
944 | |
945 | state->link = 0; |
946 | |
947 | return xpcs_do_config(xpcs, state->interface, NULL, |
948 | PHYLINK_PCS_NEG_INBAND_ENABLED); |
949 | } |
950 | |
951 | /* There is no point doing anything else if the link is down. */ |
952 | if (!state->link) |
953 | return 0; |
954 | |
955 | an_enabled = linkmode_test_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, |
956 | addr: state->advertising); |
957 | if (an_enabled) { |
958 | /* The link status bit is latching-low, so it is important to |
959 | * avoid unnecessary re-reads of this register to avoid missing |
960 | * a link-down event. |
961 | */ |
962 | an_stat1 = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); |
963 | if (an_stat1 < 0) { |
964 | state->link = false; |
965 | return an_stat1; |
966 | } |
967 | |
968 | state->an_complete = xpcs_aneg_done_c73(xpcs, state, compat, |
969 | an_stat1); |
970 | if (!state->an_complete) { |
971 | state->link = false; |
972 | return 0; |
973 | } |
974 | |
975 | ret = xpcs_read_lpa_c73(xpcs, state, an_stat1); |
976 | if (ret < 0) { |
977 | state->link = false; |
978 | return ret; |
979 | } |
980 | |
981 | phylink_resolve_c73(state); |
982 | } else { |
983 | xpcs_resolve_pma(xpcs, state); |
984 | } |
985 | |
986 | return 0; |
987 | } |
988 | |
989 | static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, |
990 | struct phylink_link_state *state) |
991 | { |
992 | int ret; |
993 | |
994 | /* Reset link_state */ |
995 | state->link = false; |
996 | state->speed = SPEED_UNKNOWN; |
997 | state->duplex = DUPLEX_UNKNOWN; |
998 | state->pause = 0; |
999 | |
1000 | /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link |
1001 | * status, speed and duplex. |
1002 | */ |
1003 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); |
1004 | if (ret < 0) |
1005 | return ret; |
1006 | |
1007 | if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { |
1008 | int speed_value; |
1009 | |
1010 | state->link = true; |
1011 | |
1012 | speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> |
1013 | DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; |
1014 | if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) |
1015 | state->speed = SPEED_1000; |
1016 | else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) |
1017 | state->speed = SPEED_100; |
1018 | else |
1019 | state->speed = SPEED_10; |
1020 | |
1021 | if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD) |
1022 | state->duplex = DUPLEX_FULL; |
1023 | else |
1024 | state->duplex = DUPLEX_HALF; |
1025 | } else if (ret == DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { |
1026 | int speed, duplex; |
1027 | |
1028 | state->link = true; |
1029 | |
1030 | speed = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); |
1031 | if (speed < 0) |
1032 | return speed; |
1033 | |
1034 | speed &= SGMII_SPEED_SS13 | SGMII_SPEED_SS6; |
1035 | if (speed == SGMII_SPEED_SS6) |
1036 | state->speed = SPEED_1000; |
1037 | else if (speed == SGMII_SPEED_SS13) |
1038 | state->speed = SPEED_100; |
1039 | else if (speed == 0) |
1040 | state->speed = SPEED_10; |
1041 | |
1042 | duplex = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_ADVERTISE); |
1043 | if (duplex < 0) |
1044 | return duplex; |
1045 | |
1046 | if (duplex & DW_FULL_DUPLEX) |
1047 | state->duplex = DUPLEX_FULL; |
1048 | else if (duplex & DW_HALF_DUPLEX) |
1049 | state->duplex = DUPLEX_HALF; |
1050 | |
1051 | xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, val: 0); |
1052 | } |
1053 | |
1054 | return 0; |
1055 | } |
1056 | |
1057 | static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs, |
1058 | struct phylink_link_state *state) |
1059 | { |
1060 | int lpa, bmsr; |
1061 | |
1062 | if (linkmode_test_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, |
1063 | addr: state->advertising)) { |
1064 | /* Reset link state */ |
1065 | state->link = false; |
1066 | |
1067 | lpa = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_LPA); |
1068 | if (lpa < 0 || lpa & LPA_RFAULT) |
1069 | return lpa; |
1070 | |
1071 | bmsr = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMSR); |
1072 | if (bmsr < 0) |
1073 | return bmsr; |
1074 | |
1075 | /* Clear AN complete interrupt */ |
1076 | if (!xpcs->pcs.poll) { |
1077 | int an_intr; |
1078 | |
1079 | an_intr = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); |
1080 | if (an_intr & DW_VR_MII_AN_STS_C37_ANCMPLT_INTR) { |
1081 | an_intr &= ~DW_VR_MII_AN_STS_C37_ANCMPLT_INTR; |
1082 | xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, val: an_intr); |
1083 | } |
1084 | } |
1085 | |
1086 | phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); |
1087 | } |
1088 | |
1089 | return 0; |
1090 | } |
1091 | |
1092 | static int xpcs_get_state_2500basex(struct dw_xpcs *xpcs, |
1093 | struct phylink_link_state *state) |
1094 | { |
1095 | int ret; |
1096 | |
1097 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_STS); |
1098 | if (ret < 0) { |
1099 | state->link = 0; |
1100 | return ret; |
1101 | } |
1102 | |
1103 | state->link = !!(ret & DW_VR_MII_MMD_STS_LINK_STS); |
1104 | if (!state->link) |
1105 | return 0; |
1106 | |
1107 | state->speed = SPEED_2500; |
1108 | state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; |
1109 | state->duplex = DUPLEX_FULL; |
1110 | |
1111 | return 0; |
1112 | } |
1113 | |
1114 | static void xpcs_get_state(struct phylink_pcs *pcs, |
1115 | struct phylink_link_state *state) |
1116 | { |
1117 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
1118 | const struct xpcs_compat *compat; |
1119 | int ret; |
1120 | |
1121 | compat = xpcs_find_compat(id: xpcs->id, interface: state->interface); |
1122 | if (!compat) |
1123 | return; |
1124 | |
1125 | switch (compat->an_mode) { |
1126 | case DW_10GBASER: |
1127 | phylink_mii_c45_pcs_get_state(pcs: xpcs->mdiodev, state); |
1128 | break; |
1129 | case DW_AN_C73: |
1130 | ret = xpcs_get_state_c73(xpcs, state, compat); |
1131 | if (ret) { |
1132 | pr_err("xpcs_get_state_c73 returned %pe\n" , |
1133 | ERR_PTR(ret)); |
1134 | return; |
1135 | } |
1136 | break; |
1137 | case DW_AN_C37_SGMII: |
1138 | ret = xpcs_get_state_c37_sgmii(xpcs, state); |
1139 | if (ret) { |
1140 | pr_err("xpcs_get_state_c37_sgmii returned %pe\n" , |
1141 | ERR_PTR(ret)); |
1142 | } |
1143 | break; |
1144 | case DW_AN_C37_1000BASEX: |
1145 | ret = xpcs_get_state_c37_1000basex(xpcs, state); |
1146 | if (ret) { |
1147 | pr_err("xpcs_get_state_c37_1000basex returned %pe\n" , |
1148 | ERR_PTR(ret)); |
1149 | } |
1150 | break; |
1151 | case DW_2500BASEX: |
1152 | ret = xpcs_get_state_2500basex(xpcs, state); |
1153 | if (ret) { |
1154 | pr_err("xpcs_get_state_2500basex returned %pe\n" , |
1155 | ERR_PTR(ret)); |
1156 | } |
1157 | break; |
1158 | default: |
1159 | return; |
1160 | } |
1161 | } |
1162 | |
1163 | static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode, |
1164 | int speed, int duplex) |
1165 | { |
1166 | int val, ret; |
1167 | |
1168 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) |
1169 | return; |
1170 | |
1171 | val = mii_bmcr_encode_fixed(speed, duplex); |
1172 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); |
1173 | if (ret) |
1174 | pr_err("%s: xpcs_write returned %pe\n" , __func__, ERR_PTR(ret)); |
1175 | } |
1176 | |
1177 | static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode, |
1178 | int speed, int duplex) |
1179 | { |
1180 | int val, ret; |
1181 | |
1182 | if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) |
1183 | return; |
1184 | |
1185 | switch (speed) { |
1186 | case SPEED_1000: |
1187 | val = BMCR_SPEED1000; |
1188 | break; |
1189 | case SPEED_100: |
1190 | case SPEED_10: |
1191 | default: |
1192 | pr_err("%s: speed = %d\n" , __func__, speed); |
1193 | return; |
1194 | } |
1195 | |
1196 | if (duplex == DUPLEX_FULL) |
1197 | val |= BMCR_FULLDPLX; |
1198 | else |
1199 | pr_err("%s: half duplex not supported\n" , __func__); |
1200 | |
1201 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); |
1202 | if (ret) |
1203 | pr_err("%s: xpcs_write returned %pe\n" , __func__, ERR_PTR(ret)); |
1204 | } |
1205 | |
1206 | void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, |
1207 | phy_interface_t interface, int speed, int duplex) |
1208 | { |
1209 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
1210 | |
1211 | if (interface == PHY_INTERFACE_MODE_USXGMII) |
1212 | return xpcs_config_usxgmii(xpcs, speed); |
1213 | if (interface == PHY_INTERFACE_MODE_SGMII) |
1214 | return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex); |
1215 | if (interface == PHY_INTERFACE_MODE_1000BASEX) |
1216 | return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex); |
1217 | } |
1218 | EXPORT_SYMBOL_GPL(xpcs_link_up); |
1219 | |
1220 | static void xpcs_an_restart(struct phylink_pcs *pcs) |
1221 | { |
1222 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
1223 | int ret; |
1224 | |
1225 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); |
1226 | if (ret >= 0) { |
1227 | ret |= BMCR_ANRESTART; |
1228 | xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val: ret); |
1229 | } |
1230 | } |
1231 | |
1232 | static u32 xpcs_get_id(struct dw_xpcs *xpcs) |
1233 | { |
1234 | int ret; |
1235 | u32 id; |
1236 | |
1237 | /* First, search C73 PCS using PCS MMD */ |
1238 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); |
1239 | if (ret < 0) |
1240 | return 0xffffffff; |
1241 | |
1242 | id = ret << 16; |
1243 | |
1244 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); |
1245 | if (ret < 0) |
1246 | return 0xffffffff; |
1247 | |
1248 | /* If Device IDs are not all zeros or all ones, |
1249 | * we found C73 AN-type device |
1250 | */ |
1251 | if ((id | ret) && (id | ret) != 0xffffffff) |
1252 | return id | ret; |
1253 | |
1254 | /* Next, search C37 PCS using Vendor-Specific MII MMD */ |
1255 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); |
1256 | if (ret < 0) |
1257 | return 0xffffffff; |
1258 | |
1259 | id = ret << 16; |
1260 | |
1261 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); |
1262 | if (ret < 0) |
1263 | return 0xffffffff; |
1264 | |
1265 | /* If Device IDs are not all zeros, we found C37 AN-type device */ |
1266 | if (id | ret) |
1267 | return id | ret; |
1268 | |
1269 | return 0xffffffff; |
1270 | } |
1271 | |
1272 | static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1273 | [DW_XPCS_USXGMII] = { |
1274 | .supported = xpcs_usxgmii_features, |
1275 | .interface = xpcs_usxgmii_interfaces, |
1276 | .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), |
1277 | .an_mode = DW_AN_C73, |
1278 | }, |
1279 | [DW_XPCS_10GKR] = { |
1280 | .supported = xpcs_10gkr_features, |
1281 | .interface = xpcs_10gkr_interfaces, |
1282 | .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), |
1283 | .an_mode = DW_AN_C73, |
1284 | }, |
1285 | [DW_XPCS_XLGMII] = { |
1286 | .supported = xpcs_xlgmii_features, |
1287 | .interface = xpcs_xlgmii_interfaces, |
1288 | .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), |
1289 | .an_mode = DW_AN_C73, |
1290 | }, |
1291 | [DW_XPCS_10GBASER] = { |
1292 | .supported = xpcs_10gbaser_features, |
1293 | .interface = xpcs_10gbaser_interfaces, |
1294 | .num_interfaces = ARRAY_SIZE(xpcs_10gbaser_interfaces), |
1295 | .an_mode = DW_10GBASER, |
1296 | }, |
1297 | [DW_XPCS_SGMII] = { |
1298 | .supported = xpcs_sgmii_features, |
1299 | .interface = xpcs_sgmii_interfaces, |
1300 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), |
1301 | .an_mode = DW_AN_C37_SGMII, |
1302 | }, |
1303 | [DW_XPCS_1000BASEX] = { |
1304 | .supported = xpcs_1000basex_features, |
1305 | .interface = xpcs_1000basex_interfaces, |
1306 | .num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces), |
1307 | .an_mode = DW_AN_C37_1000BASEX, |
1308 | }, |
1309 | [DW_XPCS_2500BASEX] = { |
1310 | .supported = xpcs_2500basex_features, |
1311 | .interface = xpcs_2500basex_interfaces, |
1312 | .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), |
1313 | .an_mode = DW_2500BASEX, |
1314 | }, |
1315 | }; |
1316 | |
1317 | static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1318 | [DW_XPCS_SGMII] = { |
1319 | .supported = xpcs_sgmii_features, |
1320 | .interface = xpcs_sgmii_interfaces, |
1321 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), |
1322 | .an_mode = DW_AN_C37_SGMII, |
1323 | .pma_config = nxp_sja1105_sgmii_pma_config, |
1324 | }, |
1325 | }; |
1326 | |
1327 | static const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1328 | [DW_XPCS_SGMII] = { |
1329 | .supported = xpcs_sgmii_features, |
1330 | .interface = xpcs_sgmii_interfaces, |
1331 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), |
1332 | .an_mode = DW_AN_C37_SGMII, |
1333 | .pma_config = nxp_sja1110_sgmii_pma_config, |
1334 | }, |
1335 | [DW_XPCS_2500BASEX] = { |
1336 | .supported = xpcs_2500basex_features, |
1337 | .interface = xpcs_2500basex_interfaces, |
1338 | .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), |
1339 | .an_mode = DW_2500BASEX, |
1340 | .pma_config = nxp_sja1110_2500basex_pma_config, |
1341 | }, |
1342 | }; |
1343 | |
1344 | static const struct xpcs_id xpcs_id_list[] = { |
1345 | { |
1346 | .id = SYNOPSYS_XPCS_ID, |
1347 | .mask = SYNOPSYS_XPCS_MASK, |
1348 | .compat = synopsys_xpcs_compat, |
1349 | }, { |
1350 | .id = NXP_SJA1105_XPCS_ID, |
1351 | .mask = SYNOPSYS_XPCS_MASK, |
1352 | .compat = nxp_sja1105_xpcs_compat, |
1353 | }, { |
1354 | .id = NXP_SJA1110_XPCS_ID, |
1355 | .mask = SYNOPSYS_XPCS_MASK, |
1356 | .compat = nxp_sja1110_xpcs_compat, |
1357 | }, |
1358 | }; |
1359 | |
1360 | static const struct phylink_pcs_ops xpcs_phylink_ops = { |
1361 | .pcs_validate = xpcs_validate, |
1362 | .pcs_config = xpcs_config, |
1363 | .pcs_get_state = xpcs_get_state, |
1364 | .pcs_an_restart = xpcs_an_restart, |
1365 | .pcs_link_up = xpcs_link_up, |
1366 | }; |
1367 | |
1368 | static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, |
1369 | phy_interface_t interface) |
1370 | { |
1371 | struct dw_xpcs *xpcs; |
1372 | u32 xpcs_id; |
1373 | int i, ret; |
1374 | |
1375 | xpcs = kzalloc(size: sizeof(*xpcs), GFP_KERNEL); |
1376 | if (!xpcs) |
1377 | return ERR_PTR(error: -ENOMEM); |
1378 | |
1379 | mdio_device_get(mdiodev); |
1380 | xpcs->mdiodev = mdiodev; |
1381 | |
1382 | xpcs_id = xpcs_get_id(xpcs); |
1383 | |
1384 | for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { |
1385 | const struct xpcs_id *entry = &xpcs_id_list[i]; |
1386 | const struct xpcs_compat *compat; |
1387 | |
1388 | if ((xpcs_id & entry->mask) != entry->id) |
1389 | continue; |
1390 | |
1391 | xpcs->id = entry; |
1392 | |
1393 | compat = xpcs_find_compat(id: entry, interface); |
1394 | if (!compat) { |
1395 | ret = -ENODEV; |
1396 | goto out; |
1397 | } |
1398 | |
1399 | ret = xpcs_dev_flag(xpcs); |
1400 | if (ret) |
1401 | goto out; |
1402 | |
1403 | xpcs->pcs.ops = &xpcs_phylink_ops; |
1404 | xpcs->pcs.neg_mode = true; |
1405 | |
1406 | if (xpcs->dev_flag != DW_DEV_TXGBE) { |
1407 | xpcs->pcs.poll = true; |
1408 | |
1409 | ret = xpcs_soft_reset(xpcs, compat); |
1410 | if (ret) |
1411 | goto out; |
1412 | } |
1413 | |
1414 | return xpcs; |
1415 | } |
1416 | |
1417 | ret = -ENODEV; |
1418 | |
1419 | out: |
1420 | mdio_device_put(mdiodev); |
1421 | kfree(objp: xpcs); |
1422 | |
1423 | return ERR_PTR(error: ret); |
1424 | } |
1425 | |
1426 | void xpcs_destroy(struct dw_xpcs *xpcs) |
1427 | { |
1428 | if (xpcs) |
1429 | mdio_device_put(mdiodev: xpcs->mdiodev); |
1430 | kfree(objp: xpcs); |
1431 | } |
1432 | EXPORT_SYMBOL_GPL(xpcs_destroy); |
1433 | |
1434 | struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, |
1435 | phy_interface_t interface) |
1436 | { |
1437 | struct mdio_device *mdiodev; |
1438 | struct dw_xpcs *xpcs; |
1439 | |
1440 | mdiodev = mdio_device_create(bus, addr); |
1441 | if (IS_ERR(ptr: mdiodev)) |
1442 | return ERR_CAST(ptr: mdiodev); |
1443 | |
1444 | xpcs = xpcs_create(mdiodev, interface); |
1445 | |
1446 | /* xpcs_create() has taken a refcount on the mdiodev if it was |
1447 | * successful. If xpcs_create() fails, this will free the mdio |
1448 | * device here. In any case, we don't need to hold our reference |
1449 | * anymore, and putting it here will allow mdio_device_put() in |
1450 | * xpcs_destroy() to automatically free the mdio device. |
1451 | */ |
1452 | mdio_device_put(mdiodev); |
1453 | |
1454 | return xpcs; |
1455 | } |
1456 | EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); |
1457 | |
1458 | MODULE_DESCRIPTION("Synopsys DesignWare XPCS library" ); |
1459 | MODULE_LICENSE("GPL v2" ); |
1460 | |