]>
Commit | Line | Data |
---|---|---|
fcb26bd2 JA |
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> | |
2fa4e4b7 | 10 | #include <linux/pcs/pcs-xpcs.h> |
fcb26bd2 | 11 | #include <linux/mdio.h> |
fcb26bd2 JA |
12 | #include <linux/phylink.h> |
13 | #include <linux/workqueue.h> | |
d4433d5b | 14 | #include "pcs-xpcs.h" |
7617af3d | 15 | |
11059740 | 16 | #define phylink_pcs_to_xpcs(pl_pcs) \ |
5673ef86 | 17 | container_of((pl_pcs), struct dw_xpcs, pcs) |
11059740 | 18 | |
fcb26bd2 JA |
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 | ||
7c6dbd29 JA |
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 | ||
b97b5331 | 67 | static const int xpcs_sgmii_features[] = { |
849d2f83 WVK |
68 | ETHTOOL_LINK_MODE_Pause_BIT, |
69 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, | |
70 | ETHTOOL_LINK_MODE_Autoneg_BIT, | |
b97b5331 OBL |
71 | ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
72 | ETHTOOL_LINK_MODE_10baseT_Full_BIT, | |
73 | ETHTOOL_LINK_MODE_100baseT_Half_BIT, | |
74 | ETHTOOL_LINK_MODE_100baseT_Full_BIT, | |
75 | ETHTOOL_LINK_MODE_1000baseT_Half_BIT, | |
76 | ETHTOOL_LINK_MODE_1000baseT_Full_BIT, | |
77 | __ETHTOOL_LINK_MODE_MASK_NBITS, | |
78 | }; | |
79 | ||
f27abde3 | 80 | static const int xpcs_2500basex_features[] = { |
849d2f83 | 81 | ETHTOOL_LINK_MODE_Pause_BIT, |
f27abde3 VW |
82 | ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
83 | ETHTOOL_LINK_MODE_Autoneg_BIT, | |
84 | ETHTOOL_LINK_MODE_2500baseX_Full_BIT, | |
85 | ETHTOOL_LINK_MODE_2500baseT_Full_BIT, | |
86 | __ETHTOOL_LINK_MODE_MASK_NBITS, | |
87 | }; | |
88 | ||
fcb26bd2 JA |
89 | static const phy_interface_t xpcs_usxgmii_interfaces[] = { |
90 | PHY_INTERFACE_MODE_USXGMII, | |
fcb26bd2 JA |
91 | }; |
92 | ||
93 | static const phy_interface_t xpcs_10gkr_interfaces[] = { | |
94 | PHY_INTERFACE_MODE_10GKR, | |
fcb26bd2 JA |
95 | }; |
96 | ||
7c6dbd29 JA |
97 | static const phy_interface_t xpcs_xlgmii_interfaces[] = { |
98 | PHY_INTERFACE_MODE_XLGMII, | |
7c6dbd29 JA |
99 | }; |
100 | ||
b97b5331 OBL |
101 | static const phy_interface_t xpcs_sgmii_interfaces[] = { |
102 | PHY_INTERFACE_MODE_SGMII, | |
b97b5331 OBL |
103 | }; |
104 | ||
f27abde3 VW |
105 | static const phy_interface_t xpcs_2500basex_interfaces[] = { |
106 | PHY_INTERFACE_MODE_2500BASEX, | |
107 | PHY_INTERFACE_MODE_MAX, | |
108 | }; | |
109 | ||
a54a8b71 VO |
110 | enum { |
111 | DW_XPCS_USXGMII, | |
112 | DW_XPCS_10GKR, | |
113 | DW_XPCS_XLGMII, | |
114 | DW_XPCS_SGMII, | |
f27abde3 | 115 | DW_XPCS_2500BASEX, |
a54a8b71 VO |
116 | DW_XPCS_INTERFACE_MAX, |
117 | }; | |
118 | ||
119 | struct xpcs_compat { | |
fcb26bd2 JA |
120 | const int *supported; |
121 | const phy_interface_t *interface; | |
a54a8b71 | 122 | int num_interfaces; |
07a4bc51 | 123 | int an_mode; |
dd0721ea | 124 | int (*pma_config)(struct dw_xpcs *xpcs); |
a54a8b71 VO |
125 | }; |
126 | ||
127 | struct xpcs_id { | |
128 | u32 id; | |
129 | u32 mask; | |
130 | const struct xpcs_compat *compat; | |
fcb26bd2 JA |
131 | }; |
132 | ||
9900074e VO |
133 | static const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id, |
134 | phy_interface_t interface) | |
135 | { | |
136 | int i, j; | |
137 | ||
138 | for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { | |
139 | const struct xpcs_compat *compat = &id->compat[i]; | |
140 | ||
141 | for (j = 0; j < compat->num_interfaces; j++) | |
142 | if (compat->interface[j] == interface) | |
143 | return compat; | |
144 | } | |
145 | ||
146 | return NULL; | |
147 | } | |
148 | ||
5673ef86 | 149 | int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface) |
9900074e VO |
150 | { |
151 | const struct xpcs_compat *compat; | |
152 | ||
153 | compat = xpcs_find_compat(xpcs->id, interface); | |
154 | if (!compat) | |
155 | return -ENODEV; | |
156 | ||
157 | return compat->an_mode; | |
158 | } | |
159 | EXPORT_SYMBOL_GPL(xpcs_get_an_mode); | |
160 | ||
161 | static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, | |
162 | enum ethtool_link_mode_bit_indices linkmode) | |
163 | { | |
164 | int i; | |
165 | ||
166 | for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) | |
167 | if (compat->supported[i] == linkmode) | |
168 | return true; | |
169 | ||
170 | return false; | |
171 | } | |
172 | ||
173 | #define xpcs_linkmode_supported(compat, mode) \ | |
174 | __xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT) | |
175 | ||
dd0721ea | 176 | int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg) |
fcb26bd2 | 177 | { |
679e283e | 178 | u32 reg_addr = mdiobus_c45_addr(dev, reg); |
2cac15da VO |
179 | struct mii_bus *bus = xpcs->mdiodev->bus; |
180 | int addr = xpcs->mdiodev->addr; | |
fcb26bd2 | 181 | |
2cac15da | 182 | return mdiobus_read(bus, addr, reg_addr); |
fcb26bd2 JA |
183 | } |
184 | ||
dd0721ea | 185 | int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val) |
fcb26bd2 | 186 | { |
679e283e | 187 | u32 reg_addr = mdiobus_c45_addr(dev, reg); |
2cac15da VO |
188 | struct mii_bus *bus = xpcs->mdiodev->bus; |
189 | int addr = xpcs->mdiodev->addr; | |
fcb26bd2 | 190 | |
2cac15da | 191 | return mdiobus_write(bus, addr, reg_addr, val); |
fcb26bd2 JA |
192 | } |
193 | ||
5673ef86 | 194 | static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg) |
fcb26bd2 JA |
195 | { |
196 | return xpcs_read(xpcs, dev, DW_VENDOR | reg); | |
197 | } | |
198 | ||
5673ef86 | 199 | static int xpcs_write_vendor(struct dw_xpcs *xpcs, int dev, int reg, |
fcb26bd2 JA |
200 | u16 val) |
201 | { | |
202 | return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); | |
203 | } | |
204 | ||
5673ef86 | 205 | static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) |
fcb26bd2 JA |
206 | { |
207 | return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); | |
208 | } | |
209 | ||
5673ef86 | 210 | static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) |
fcb26bd2 JA |
211 | { |
212 | return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); | |
213 | } | |
214 | ||
5673ef86 | 215 | static int xpcs_poll_reset(struct dw_xpcs *xpcs, int dev) |
fcb26bd2 JA |
216 | { |
217 | /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ | |
218 | unsigned int retries = 12; | |
219 | int ret; | |
220 | ||
221 | do { | |
222 | msleep(50); | |
223 | ret = xpcs_read(xpcs, dev, MDIO_CTRL1); | |
224 | if (ret < 0) | |
225 | return ret; | |
226 | } while (ret & MDIO_CTRL1_RESET && --retries); | |
227 | ||
228 | return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; | |
229 | } | |
230 | ||
5673ef86 | 231 | static int xpcs_soft_reset(struct dw_xpcs *xpcs, |
9900074e | 232 | const struct xpcs_compat *compat) |
fcb26bd2 | 233 | { |
07a4bc51 OBL |
234 | int ret, dev; |
235 | ||
9900074e | 236 | switch (compat->an_mode) { |
07a4bc51 OBL |
237 | case DW_AN_C73: |
238 | dev = MDIO_MMD_PCS; | |
239 | break; | |
b97b5331 | 240 | case DW_AN_C37_SGMII: |
f27abde3 | 241 | case DW_2500BASEX: |
b97b5331 OBL |
242 | dev = MDIO_MMD_VEND2; |
243 | break; | |
07a4bc51 OBL |
244 | default: |
245 | return -1; | |
246 | } | |
fcb26bd2 JA |
247 | |
248 | ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET); | |
249 | if (ret < 0) | |
250 | return ret; | |
251 | ||
252 | return xpcs_poll_reset(xpcs, dev); | |
253 | } | |
254 | ||
255 | #define xpcs_warn(__xpcs, __state, __args...) \ | |
256 | ({ \ | |
257 | if ((__state)->link) \ | |
2cac15da | 258 | dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \ |
fcb26bd2 JA |
259 | }) |
260 | ||
5673ef86 | 261 | static int xpcs_read_fault_c73(struct dw_xpcs *xpcs, |
07a4bc51 | 262 | struct phylink_link_state *state) |
fcb26bd2 JA |
263 | { |
264 | int ret; | |
265 | ||
266 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); | |
267 | if (ret < 0) | |
268 | return ret; | |
269 | ||
270 | if (ret & MDIO_STAT1_FAULT) { | |
271 | xpcs_warn(xpcs, state, "Link fault condition detected!\n"); | |
272 | return -EFAULT; | |
273 | } | |
274 | ||
275 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2); | |
276 | if (ret < 0) | |
277 | return ret; | |
278 | ||
279 | if (ret & MDIO_STAT2_RXFAULT) | |
280 | xpcs_warn(xpcs, state, "Receiver fault detected!\n"); | |
281 | if (ret & MDIO_STAT2_TXFAULT) | |
282 | xpcs_warn(xpcs, state, "Transmitter fault detected!\n"); | |
283 | ||
284 | ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS); | |
285 | if (ret < 0) | |
286 | return ret; | |
287 | ||
288 | if (ret & DW_RXFIFO_ERR) { | |
289 | xpcs_warn(xpcs, state, "FIFO fault condition detected!\n"); | |
290 | return -EFAULT; | |
291 | } | |
292 | ||
293 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); | |
294 | if (ret < 0) | |
295 | return ret; | |
296 | ||
297 | if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK)) | |
298 | xpcs_warn(xpcs, state, "Link is not locked!\n"); | |
299 | ||
300 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2); | |
301 | if (ret < 0) | |
302 | return ret; | |
303 | ||
f84752d0 | 304 | if (ret & MDIO_PCS_10GBRT_STAT2_ERR) { |
fcb26bd2 | 305 | xpcs_warn(xpcs, state, "Link has errors!\n"); |
f84752d0 JA |
306 | return -EFAULT; |
307 | } | |
fcb26bd2 JA |
308 | |
309 | return 0; | |
310 | } | |
311 | ||
5673ef86 | 312 | static int xpcs_read_link_c73(struct dw_xpcs *xpcs, bool an) |
fcb26bd2 JA |
313 | { |
314 | bool link = true; | |
315 | int ret; | |
316 | ||
317 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); | |
318 | if (ret < 0) | |
319 | return ret; | |
320 | ||
321 | if (!(ret & MDIO_STAT1_LSTATUS)) | |
322 | link = false; | |
323 | ||
324 | if (an) { | |
325 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); | |
326 | if (ret < 0) | |
327 | return ret; | |
328 | ||
329 | if (!(ret & MDIO_STAT1_LSTATUS)) | |
330 | link = false; | |
331 | } | |
332 | ||
333 | return link; | |
334 | } | |
335 | ||
336 | static int xpcs_get_max_usxgmii_speed(const unsigned long *supported) | |
337 | { | |
338 | int max = SPEED_UNKNOWN; | |
339 | ||
340 | if (phylink_test(supported, 1000baseKX_Full)) | |
341 | max = SPEED_1000; | |
342 | if (phylink_test(supported, 2500baseX_Full)) | |
343 | max = SPEED_2500; | |
344 | if (phylink_test(supported, 10000baseKX4_Full)) | |
345 | max = SPEED_10000; | |
346 | if (phylink_test(supported, 10000baseKR_Full)) | |
347 | max = SPEED_10000; | |
348 | ||
349 | return max; | |
350 | } | |
351 | ||
5673ef86 | 352 | static void xpcs_config_usxgmii(struct dw_xpcs *xpcs, int speed) |
fcb26bd2 JA |
353 | { |
354 | int ret, speed_sel; | |
355 | ||
356 | switch (speed) { | |
357 | case SPEED_10: | |
358 | speed_sel = DW_USXGMII_10; | |
359 | break; | |
360 | case SPEED_100: | |
361 | speed_sel = DW_USXGMII_100; | |
362 | break; | |
363 | case SPEED_1000: | |
364 | speed_sel = DW_USXGMII_1000; | |
365 | break; | |
366 | case SPEED_2500: | |
367 | speed_sel = DW_USXGMII_2500; | |
368 | break; | |
369 | case SPEED_5000: | |
370 | speed_sel = DW_USXGMII_5000; | |
371 | break; | |
372 | case SPEED_10000: | |
373 | speed_sel = DW_USXGMII_10000; | |
374 | break; | |
375 | default: | |
376 | /* Nothing to do here */ | |
11059740 | 377 | return; |
fcb26bd2 JA |
378 | } |
379 | ||
380 | ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); | |
381 | if (ret < 0) | |
11059740 | 382 | goto out; |
fcb26bd2 JA |
383 | |
384 | ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); | |
385 | if (ret < 0) | |
11059740 | 386 | goto out; |
fcb26bd2 JA |
387 | |
388 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); | |
389 | if (ret < 0) | |
11059740 | 390 | goto out; |
fcb26bd2 JA |
391 | |
392 | ret &= ~DW_USXGMII_SS_MASK; | |
393 | ret |= speed_sel | DW_USXGMII_FULL; | |
394 | ||
395 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); | |
396 | if (ret < 0) | |
11059740 | 397 | goto out; |
fcb26bd2 JA |
398 | |
399 | ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); | |
400 | if (ret < 0) | |
11059740 VO |
401 | goto out; |
402 | ||
403 | ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); | |
404 | if (ret < 0) | |
405 | goto out; | |
406 | ||
407 | return; | |
fcb26bd2 | 408 | |
11059740 VO |
409 | out: |
410 | pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret)); | |
fcb26bd2 JA |
411 | } |
412 | ||
5673ef86 | 413 | static int _xpcs_config_aneg_c73(struct dw_xpcs *xpcs, |
9900074e | 414 | const struct xpcs_compat *compat) |
fcb26bd2 JA |
415 | { |
416 | int ret, adv; | |
417 | ||
418 | /* By default, in USXGMII mode XPCS operates at 10G baud and | |
419 | * replicates data to achieve lower speeds. Hereby, in this | |
420 | * default configuration we need to advertise all supported | |
421 | * modes and not only the ones we want to use. | |
422 | */ | |
423 | ||
424 | /* SR_AN_ADV3 */ | |
425 | adv = 0; | |
9900074e | 426 | if (xpcs_linkmode_supported(compat, 2500baseX_Full)) |
fcb26bd2 JA |
427 | adv |= DW_C73_2500KX; |
428 | ||
429 | /* TODO: 5000baseKR */ | |
430 | ||
431 | ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv); | |
432 | if (ret < 0) | |
433 | return ret; | |
434 | ||
435 | /* SR_AN_ADV2 */ | |
436 | adv = 0; | |
9900074e | 437 | if (xpcs_linkmode_supported(compat, 1000baseKX_Full)) |
fcb26bd2 | 438 | adv |= DW_C73_1000KX; |
9900074e | 439 | if (xpcs_linkmode_supported(compat, 10000baseKX4_Full)) |
fcb26bd2 | 440 | adv |= DW_C73_10000KX4; |
9900074e | 441 | if (xpcs_linkmode_supported(compat, 10000baseKR_Full)) |
fcb26bd2 JA |
442 | adv |= DW_C73_10000KR; |
443 | ||
444 | ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv); | |
445 | if (ret < 0) | |
446 | return ret; | |
447 | ||
448 | /* SR_AN_ADV1 */ | |
449 | adv = DW_C73_AN_ADV_SF; | |
9900074e | 450 | if (xpcs_linkmode_supported(compat, Pause)) |
fcb26bd2 | 451 | adv |= DW_C73_PAUSE; |
9900074e | 452 | if (xpcs_linkmode_supported(compat, Asym_Pause)) |
fcb26bd2 JA |
453 | adv |= DW_C73_ASYM_PAUSE; |
454 | ||
455 | return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv); | |
456 | } | |
457 | ||
5673ef86 | 458 | static int xpcs_config_aneg_c73(struct dw_xpcs *xpcs, |
9900074e | 459 | const struct xpcs_compat *compat) |
fcb26bd2 JA |
460 | { |
461 | int ret; | |
462 | ||
9900074e | 463 | ret = _xpcs_config_aneg_c73(xpcs, compat); |
fcb26bd2 JA |
464 | if (ret < 0) |
465 | return ret; | |
466 | ||
467 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); | |
468 | if (ret < 0) | |
469 | return ret; | |
470 | ||
471 | ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; | |
472 | ||
473 | return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret); | |
474 | } | |
475 | ||
5673ef86 | 476 | static int xpcs_aneg_done_c73(struct dw_xpcs *xpcs, |
9900074e VO |
477 | struct phylink_link_state *state, |
478 | const struct xpcs_compat *compat) | |
fcb26bd2 JA |
479 | { |
480 | int ret; | |
481 | ||
482 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); | |
483 | if (ret < 0) | |
484 | return ret; | |
485 | ||
486 | if (ret & MDIO_AN_STAT1_COMPLETE) { | |
487 | ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); | |
488 | if (ret < 0) | |
489 | return ret; | |
490 | ||
491 | /* Check if Aneg outcome is valid */ | |
1874b83a | 492 | if (!(ret & DW_C73_AN_ADV_SF)) { |
9900074e | 493 | xpcs_config_aneg_c73(xpcs, compat); |
fcb26bd2 | 494 | return 0; |
1874b83a | 495 | } |
fcb26bd2 JA |
496 | |
497 | return 1; | |
498 | } | |
499 | ||
500 | return 0; | |
501 | } | |
502 | ||
5673ef86 | 503 | static int xpcs_read_lpa_c73(struct dw_xpcs *xpcs, |
07a4bc51 | 504 | struct phylink_link_state *state) |
fcb26bd2 JA |
505 | { |
506 | int ret; | |
507 | ||
508 | ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); | |
509 | if (ret < 0) | |
510 | return ret; | |
511 | ||
512 | if (!(ret & 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 | /* Clause 73 outcome */ | |
520 | ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL3); | |
521 | if (ret < 0) | |
522 | return ret; | |
523 | ||
524 | if (ret & DW_C73_2500KX) | |
525 | phylink_set(state->lp_advertising, 2500baseX_Full); | |
526 | ||
527 | ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL2); | |
528 | if (ret < 0) | |
529 | return ret; | |
530 | ||
531 | if (ret & DW_C73_1000KX) | |
532 | phylink_set(state->lp_advertising, 1000baseKX_Full); | |
533 | if (ret & DW_C73_10000KX4) | |
534 | phylink_set(state->lp_advertising, 10000baseKX4_Full); | |
535 | if (ret & DW_C73_10000KR) | |
536 | phylink_set(state->lp_advertising, 10000baseKR_Full); | |
537 | ||
538 | ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); | |
539 | if (ret < 0) | |
540 | return ret; | |
541 | ||
542 | if (ret & DW_C73_PAUSE) | |
543 | phylink_set(state->lp_advertising, Pause); | |
544 | if (ret & DW_C73_ASYM_PAUSE) | |
545 | phylink_set(state->lp_advertising, Asym_Pause); | |
546 | ||
547 | linkmode_and(state->lp_advertising, state->lp_advertising, | |
548 | state->advertising); | |
549 | return 0; | |
550 | } | |
551 | ||
5673ef86 | 552 | static void xpcs_resolve_lpa_c73(struct dw_xpcs *xpcs, |
07a4bc51 | 553 | struct phylink_link_state *state) |
fcb26bd2 JA |
554 | { |
555 | int max_speed = xpcs_get_max_usxgmii_speed(state->lp_advertising); | |
556 | ||
557 | state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; | |
558 | state->speed = max_speed; | |
559 | state->duplex = DUPLEX_FULL; | |
560 | } | |
561 | ||
5673ef86 | 562 | static int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs, |
7c6dbd29 JA |
563 | struct phylink_link_state *state) |
564 | { | |
565 | unsigned long *adv = state->advertising; | |
566 | int speed = SPEED_UNKNOWN; | |
567 | int bit; | |
568 | ||
569 | for_each_set_bit(bit, adv, __ETHTOOL_LINK_MODE_MASK_NBITS) { | |
570 | int new_speed = SPEED_UNKNOWN; | |
571 | ||
572 | switch (bit) { | |
573 | case ETHTOOL_LINK_MODE_25000baseCR_Full_BIT: | |
574 | case ETHTOOL_LINK_MODE_25000baseKR_Full_BIT: | |
575 | case ETHTOOL_LINK_MODE_25000baseSR_Full_BIT: | |
576 | new_speed = SPEED_25000; | |
577 | break; | |
578 | case ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT: | |
579 | case ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT: | |
580 | case ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT: | |
581 | case ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT: | |
582 | new_speed = SPEED_40000; | |
583 | break; | |
584 | case ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT: | |
585 | case ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT: | |
586 | case ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT: | |
587 | case ETHTOOL_LINK_MODE_50000baseKR_Full_BIT: | |
588 | case ETHTOOL_LINK_MODE_50000baseSR_Full_BIT: | |
589 | case ETHTOOL_LINK_MODE_50000baseCR_Full_BIT: | |
590 | case ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT: | |
591 | case ETHTOOL_LINK_MODE_50000baseDR_Full_BIT: | |
592 | new_speed = SPEED_50000; | |
593 | break; | |
594 | case ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT: | |
595 | case ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT: | |
596 | case ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT: | |
597 | case ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT: | |
598 | case ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT: | |
599 | case ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT: | |
600 | case ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT: | |
601 | case ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT: | |
602 | case ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT: | |
603 | new_speed = SPEED_100000; | |
604 | break; | |
605 | default: | |
606 | continue; | |
607 | } | |
608 | ||
609 | if (new_speed > speed) | |
610 | speed = new_speed; | |
611 | } | |
612 | ||
613 | return speed; | |
614 | } | |
615 | ||
5673ef86 | 616 | static void xpcs_resolve_pma(struct dw_xpcs *xpcs, |
fcb26bd2 JA |
617 | struct phylink_link_state *state) |
618 | { | |
619 | state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; | |
620 | state->duplex = DUPLEX_FULL; | |
621 | ||
622 | switch (state->interface) { | |
623 | case PHY_INTERFACE_MODE_10GKR: | |
624 | state->speed = SPEED_10000; | |
625 | break; | |
7c6dbd29 JA |
626 | case PHY_INTERFACE_MODE_XLGMII: |
627 | state->speed = xpcs_get_max_xlgmii_speed(xpcs, state); | |
628 | break; | |
fcb26bd2 JA |
629 | default: |
630 | state->speed = SPEED_UNKNOWN; | |
631 | break; | |
632 | } | |
633 | } | |
634 | ||
5673ef86 | 635 | void xpcs_validate(struct dw_xpcs *xpcs, unsigned long *supported, |
a1a753ed | 636 | struct phylink_link_state *state) |
fcb26bd2 | 637 | { |
9900074e VO |
638 | __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported); |
639 | const struct xpcs_compat *compat; | |
640 | int i; | |
641 | ||
642 | /* phylink expects us to report all supported modes with | |
643 | * PHY_INTERFACE_MODE_NA, just don't limit the supported and | |
644 | * advertising masks and exit. | |
645 | */ | |
646 | if (state->interface == PHY_INTERFACE_MODE_NA) | |
a1a753ed | 647 | return; |
9900074e VO |
648 | |
649 | bitmap_zero(xpcs_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
650 | ||
651 | compat = xpcs_find_compat(xpcs->id, state->interface); | |
652 | ||
653 | /* Populate the supported link modes for this | |
654 | * PHY interface type | |
655 | */ | |
656 | if (compat) | |
657 | for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) | |
658 | set_bit(compat->supported[i], xpcs_supported); | |
659 | ||
660 | linkmode_and(supported, supported, xpcs_supported); | |
661 | linkmode_and(state->advertising, state->advertising, xpcs_supported); | |
fcb26bd2 | 662 | } |
a1a753ed | 663 | EXPORT_SYMBOL_GPL(xpcs_validate); |
fcb26bd2 | 664 | |
5673ef86 | 665 | int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable) |
7617af3d MSWH |
666 | { |
667 | int ret; | |
668 | ||
590df78b WVK |
669 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0); |
670 | if (ret < 0) | |
671 | return ret; | |
672 | ||
7617af3d MSWH |
673 | if (enable) { |
674 | /* Enable EEE */ | |
675 | ret = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | | |
676 | DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | | |
677 | DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | | |
678 | mult_fact_100ns << DW_VR_MII_EEE_MULT_FACT_100NS_SHIFT; | |
679 | } else { | |
7617af3d MSWH |
680 | ret &= ~(DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN | |
681 | DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN | | |
682 | DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL | | |
683 | DW_VR_MII_EEE_MULT_FACT_100NS); | |
684 | } | |
685 | ||
686 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, ret); | |
687 | if (ret < 0) | |
688 | return ret; | |
689 | ||
690 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1); | |
691 | if (ret < 0) | |
692 | return ret; | |
693 | ||
590df78b WVK |
694 | if (enable) |
695 | ret |= DW_VR_MII_EEE_TRN_LPI; | |
696 | else | |
697 | ret &= ~DW_VR_MII_EEE_TRN_LPI; | |
698 | ||
7617af3d MSWH |
699 | return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret); |
700 | } | |
14b517cb | 701 | EXPORT_SYMBOL_GPL(xpcs_config_eee); |
7617af3d | 702 | |
2031c09e | 703 | static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode) |
b97b5331 | 704 | { |
e3cf002d | 705 | int ret, mdio_ctrl; |
b97b5331 OBL |
706 | |
707 | /* For AN for C37 SGMII mode, the settings are :- | |
e3cf002d WVK |
708 | * 1) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 0b (Disable SGMII AN in case |
709 | it is already enabled) | |
710 | * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN) | |
711 | * 3) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII) | |
b97b5331 | 712 | * DW xPCS used with DW EQoS MAC is always MAC side SGMII. |
e3cf002d | 713 | * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic |
b97b5331 | 714 | * speed/duplex mode change by HW after SGMII AN complete) |
e3cf002d | 715 | * 5) VR_MII_MMD_CTRL Bit(12) [AN_ENABLE] = 1b (Enable SGMII AN) |
b97b5331 OBL |
716 | * |
717 | * Note: Since it is MAC side SGMII, there is no need to set | |
718 | * SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config from | |
719 | * PHY about the link state change after C28 AN is completed | |
720 | * between PHY and Link Partner. There is also no need to | |
721 | * trigger AN restart for MAC-side SGMII. | |
722 | */ | |
e3cf002d WVK |
723 | mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); |
724 | if (mdio_ctrl < 0) | |
725 | return mdio_ctrl; | |
726 | ||
727 | if (mdio_ctrl & AN_CL37_EN) { | |
728 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, | |
729 | mdio_ctrl & ~AN_CL37_EN); | |
730 | if (ret < 0) | |
731 | return ret; | |
732 | } | |
733 | ||
b97b5331 OBL |
734 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); |
735 | if (ret < 0) | |
736 | return ret; | |
737 | ||
738 | ret &= ~(DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_TX_CONFIG_MASK); | |
739 | ret |= (DW_VR_MII_PCS_MODE_C37_SGMII << | |
740 | DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT & | |
741 | DW_VR_MII_PCS_MODE_MASK); | |
742 | ret |= (DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII << | |
743 | DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT & | |
744 | DW_VR_MII_TX_CONFIG_MASK); | |
745 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); | |
746 | if (ret < 0) | |
747 | return ret; | |
748 | ||
749 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); | |
750 | if (ret < 0) | |
751 | return ret; | |
752 | ||
2031c09e VO |
753 | if (phylink_autoneg_inband(mode)) |
754 | ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; | |
755 | else | |
756 | ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; | |
b97b5331 | 757 | |
e3cf002d WVK |
758 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); |
759 | if (ret < 0) | |
760 | return ret; | |
761 | ||
762 | if (phylink_autoneg_inband(mode)) | |
763 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, | |
764 | mdio_ctrl | AN_CL37_EN); | |
765 | ||
766 | return ret; | |
b97b5331 OBL |
767 | } |
768 | ||
5673ef86 | 769 | static int xpcs_config_2500basex(struct dw_xpcs *xpcs) |
f27abde3 VW |
770 | { |
771 | int ret; | |
772 | ||
773 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1); | |
774 | if (ret < 0) | |
775 | return ret; | |
776 | ret |= DW_VR_MII_DIG_CTRL1_2G5_EN; | |
777 | ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW; | |
778 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); | |
779 | if (ret < 0) | |
780 | return ret; | |
781 | ||
782 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); | |
783 | if (ret < 0) | |
784 | return ret; | |
785 | ret &= ~AN_CL37_EN; | |
786 | ret |= SGMII_SPEED_SS6; | |
787 | ret &= ~SGMII_SPEED_SS13; | |
788 | return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, ret); | |
789 | } | |
790 | ||
a853c68e VO |
791 | int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface, |
792 | unsigned int mode) | |
fcb26bd2 | 793 | { |
9900074e | 794 | const struct xpcs_compat *compat; |
fcb26bd2 JA |
795 | int ret; |
796 | ||
11059740 | 797 | compat = xpcs_find_compat(xpcs->id, interface); |
9900074e VO |
798 | if (!compat) |
799 | return -ENODEV; | |
800 | ||
801 | switch (compat->an_mode) { | |
07a4bc51 | 802 | case DW_AN_C73: |
11059740 | 803 | if (phylink_autoneg_inband(mode)) { |
9900074e | 804 | ret = xpcs_config_aneg_c73(xpcs, compat); |
07a4bc51 OBL |
805 | if (ret) |
806 | return ret; | |
807 | } | |
808 | break; | |
b97b5331 | 809 | case DW_AN_C37_SGMII: |
2031c09e | 810 | ret = xpcs_config_aneg_c37_sgmii(xpcs, mode); |
b97b5331 OBL |
811 | if (ret) |
812 | return ret; | |
813 | break; | |
f27abde3 VW |
814 | case DW_2500BASEX: |
815 | ret = xpcs_config_2500basex(xpcs); | |
816 | if (ret) | |
817 | return ret; | |
818 | break; | |
07a4bc51 OBL |
819 | default: |
820 | return -1; | |
fcb26bd2 JA |
821 | } |
822 | ||
dd0721ea VO |
823 | if (compat->pma_config) { |
824 | ret = compat->pma_config(xpcs); | |
825 | if (ret) | |
826 | return ret; | |
827 | } | |
828 | ||
fcb26bd2 JA |
829 | return 0; |
830 | } | |
a853c68e | 831 | EXPORT_SYMBOL_GPL(xpcs_do_config); |
fcb26bd2 | 832 | |
11059740 VO |
833 | static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode, |
834 | phy_interface_t interface, | |
835 | const unsigned long *advertising, | |
836 | bool permit_pause_to_mac) | |
837 | { | |
5673ef86 | 838 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
11059740 VO |
839 | |
840 | return xpcs_do_config(xpcs, interface, mode); | |
841 | } | |
842 | ||
5673ef86 | 843 | static int xpcs_get_state_c73(struct dw_xpcs *xpcs, |
9900074e VO |
844 | struct phylink_link_state *state, |
845 | const struct xpcs_compat *compat) | |
fcb26bd2 JA |
846 | { |
847 | int ret; | |
848 | ||
849 | /* Link needs to be read first ... */ | |
07a4bc51 | 850 | state->link = xpcs_read_link_c73(xpcs, state->an_enabled) > 0 ? 1 : 0; |
fcb26bd2 JA |
851 | |
852 | /* ... and then we check the faults. */ | |
07a4bc51 | 853 | ret = xpcs_read_fault_c73(xpcs, state); |
fcb26bd2 | 854 | if (ret) { |
9900074e | 855 | ret = xpcs_soft_reset(xpcs, compat); |
fcb26bd2 JA |
856 | if (ret) |
857 | return ret; | |
858 | ||
859 | state->link = 0; | |
860 | ||
11059740 | 861 | return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND); |
fcb26bd2 JA |
862 | } |
863 | ||
9900074e | 864 | if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) { |
fcb26bd2 | 865 | state->an_complete = true; |
07a4bc51 OBL |
866 | xpcs_read_lpa_c73(xpcs, state); |
867 | xpcs_resolve_lpa_c73(xpcs, state); | |
e1eab7df JA |
868 | } else if (state->an_enabled) { |
869 | state->link = 0; | |
fcb26bd2 JA |
870 | } else if (state->link) { |
871 | xpcs_resolve_pma(xpcs, state); | |
872 | } | |
873 | ||
874 | return 0; | |
875 | } | |
876 | ||
5673ef86 | 877 | static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs, |
b97b5331 OBL |
878 | struct phylink_link_state *state) |
879 | { | |
880 | int ret; | |
881 | ||
882 | /* Reset link_state */ | |
883 | state->link = false; | |
884 | state->speed = SPEED_UNKNOWN; | |
885 | state->duplex = DUPLEX_UNKNOWN; | |
886 | state->pause = 0; | |
887 | ||
888 | /* For C37 SGMII mode, we check DW_VR_MII_AN_INTR_STS for link | |
889 | * status, speed and duplex. | |
890 | */ | |
891 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS); | |
892 | if (ret < 0) | |
d8d4e037 | 893 | return ret; |
b97b5331 OBL |
894 | |
895 | if (ret & DW_VR_MII_C37_ANSGM_SP_LNKSTS) { | |
896 | int speed_value; | |
897 | ||
898 | state->link = true; | |
899 | ||
900 | speed_value = (ret & DW_VR_MII_AN_STS_C37_ANSGM_SP) >> | |
901 | DW_VR_MII_AN_STS_C37_ANSGM_SP_SHIFT; | |
902 | if (speed_value == DW_VR_MII_C37_ANSGM_SP_1000) | |
903 | state->speed = SPEED_1000; | |
904 | else if (speed_value == DW_VR_MII_C37_ANSGM_SP_100) | |
905 | state->speed = SPEED_100; | |
906 | else | |
907 | state->speed = SPEED_10; | |
908 | ||
909 | if (ret & DW_VR_MII_AN_STS_C37_ANSGM_FD) | |
910 | state->duplex = DUPLEX_FULL; | |
911 | else | |
912 | state->duplex = DUPLEX_HALF; | |
913 | } | |
914 | ||
915 | return 0; | |
916 | } | |
917 | ||
11059740 VO |
918 | static void xpcs_get_state(struct phylink_pcs *pcs, |
919 | struct phylink_link_state *state) | |
07a4bc51 | 920 | { |
5673ef86 | 921 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
9900074e | 922 | const struct xpcs_compat *compat; |
07a4bc51 OBL |
923 | int ret; |
924 | ||
9900074e VO |
925 | compat = xpcs_find_compat(xpcs->id, state->interface); |
926 | if (!compat) | |
11059740 | 927 | return; |
9900074e VO |
928 | |
929 | switch (compat->an_mode) { | |
07a4bc51 | 930 | case DW_AN_C73: |
9900074e | 931 | ret = xpcs_get_state_c73(xpcs, state, compat); |
11059740 VO |
932 | if (ret) { |
933 | pr_err("xpcs_get_state_c73 returned %pe\n", | |
934 | ERR_PTR(ret)); | |
935 | return; | |
936 | } | |
07a4bc51 | 937 | break; |
b97b5331 OBL |
938 | case DW_AN_C37_SGMII: |
939 | ret = xpcs_get_state_c37_sgmii(xpcs, state); | |
11059740 VO |
940 | if (ret) { |
941 | pr_err("xpcs_get_state_c37_sgmii returned %pe\n", | |
942 | ERR_PTR(ret)); | |
943 | } | |
b97b5331 | 944 | break; |
07a4bc51 | 945 | default: |
11059740 | 946 | return; |
07a4bc51 | 947 | } |
07a4bc51 OBL |
948 | } |
949 | ||
2031c09e VO |
950 | static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode, |
951 | int speed, int duplex) | |
952 | { | |
953 | int val, ret; | |
954 | ||
955 | if (phylink_autoneg_inband(mode)) | |
956 | return; | |
957 | ||
958 | switch (speed) { | |
959 | case SPEED_1000: | |
960 | val = BMCR_SPEED1000; | |
961 | break; | |
962 | case SPEED_100: | |
963 | val = BMCR_SPEED100; | |
964 | break; | |
965 | case SPEED_10: | |
966 | val = BMCR_SPEED10; | |
967 | break; | |
968 | default: | |
969 | return; | |
970 | } | |
971 | ||
972 | if (duplex == DUPLEX_FULL) | |
973 | val |= BMCR_FULLDPLX; | |
974 | ||
975 | ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); | |
976 | if (ret) | |
977 | pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); | |
978 | } | |
979 | ||
a853c68e VO |
980 | void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode, |
981 | phy_interface_t interface, int speed, int duplex) | |
fcb26bd2 | 982 | { |
5673ef86 | 983 | struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); |
11059740 | 984 | |
fcb26bd2 JA |
985 | if (interface == PHY_INTERFACE_MODE_USXGMII) |
986 | return xpcs_config_usxgmii(xpcs, speed); | |
2031c09e VO |
987 | if (interface == PHY_INTERFACE_MODE_SGMII) |
988 | return xpcs_link_up_sgmii(xpcs, mode, speed, duplex); | |
fcb26bd2 | 989 | } |
a853c68e | 990 | EXPORT_SYMBOL_GPL(xpcs_link_up); |
fcb26bd2 | 991 | |
5673ef86 | 992 | static u32 xpcs_get_id(struct dw_xpcs *xpcs) |
fcb26bd2 JA |
993 | { |
994 | int ret; | |
995 | u32 id; | |
996 | ||
b97b5331 | 997 | /* First, search C73 PCS using PCS MMD */ |
fcb26bd2 JA |
998 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); |
999 | if (ret < 0) | |
1000 | return 0xffffffff; | |
1001 | ||
1002 | id = ret << 16; | |
1003 | ||
1004 | ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); | |
1005 | if (ret < 0) | |
1006 | return 0xffffffff; | |
1007 | ||
36641b04 VO |
1008 | /* If Device IDs are not all zeros or all ones, |
1009 | * we found C73 AN-type device | |
1010 | */ | |
1011 | if ((id | ret) && (id | ret) != 0xffffffff) | |
b97b5331 OBL |
1012 | return id | ret; |
1013 | ||
1014 | /* Next, search C37 PCS using Vendor-Specific MII MMD */ | |
1015 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID1); | |
1016 | if (ret < 0) | |
1017 | return 0xffffffff; | |
1018 | ||
1019 | id = ret << 16; | |
1020 | ||
1021 | ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_PHYSID2); | |
1022 | if (ret < 0) | |
1023 | return 0xffffffff; | |
1024 | ||
1025 | /* If Device IDs are not all zeros, we found C37 AN-type device */ | |
1026 | if (id | ret) | |
1027 | return id | ret; | |
1028 | ||
1029 | return 0xffffffff; | |
fcb26bd2 JA |
1030 | } |
1031 | ||
a54a8b71 VO |
1032 | static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1033 | [DW_XPCS_USXGMII] = { | |
1034 | .supported = xpcs_usxgmii_features, | |
1035 | .interface = xpcs_usxgmii_interfaces, | |
1036 | .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), | |
1037 | .an_mode = DW_AN_C73, | |
1038 | }, | |
1039 | [DW_XPCS_10GKR] = { | |
1040 | .supported = xpcs_10gkr_features, | |
1041 | .interface = xpcs_10gkr_interfaces, | |
1042 | .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), | |
1043 | .an_mode = DW_AN_C73, | |
1044 | }, | |
1045 | [DW_XPCS_XLGMII] = { | |
1046 | .supported = xpcs_xlgmii_features, | |
1047 | .interface = xpcs_xlgmii_interfaces, | |
1048 | .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), | |
1049 | .an_mode = DW_AN_C73, | |
1050 | }, | |
1051 | [DW_XPCS_SGMII] = { | |
1052 | .supported = xpcs_sgmii_features, | |
1053 | .interface = xpcs_sgmii_interfaces, | |
1054 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), | |
1055 | .an_mode = DW_AN_C37_SGMII, | |
1056 | }, | |
f27abde3 VW |
1057 | [DW_XPCS_2500BASEX] = { |
1058 | .supported = xpcs_2500basex_features, | |
1059 | .interface = xpcs_2500basex_interfaces, | |
1060 | .num_interfaces = ARRAY_SIZE(xpcs_2500basex_features), | |
1061 | .an_mode = DW_2500BASEX, | |
1062 | }, | |
a54a8b71 VO |
1063 | }; |
1064 | ||
dd0721ea VO |
1065 | static const struct xpcs_compat nxp_sja1105_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1066 | [DW_XPCS_SGMII] = { | |
1067 | .supported = xpcs_sgmii_features, | |
1068 | .interface = xpcs_sgmii_interfaces, | |
1069 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), | |
1070 | .an_mode = DW_AN_C37_SGMII, | |
1071 | .pma_config = nxp_sja1105_sgmii_pma_config, | |
1072 | }, | |
1073 | }; | |
1074 | ||
f7380bba VO |
1075 | static const struct xpcs_compat nxp_sja1110_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { |
1076 | [DW_XPCS_SGMII] = { | |
1077 | .supported = xpcs_sgmii_features, | |
1078 | .interface = xpcs_sgmii_interfaces, | |
1079 | .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), | |
1080 | .an_mode = DW_AN_C37_SGMII, | |
1081 | .pma_config = nxp_sja1110_sgmii_pma_config, | |
1082 | }, | |
1083 | [DW_XPCS_2500BASEX] = { | |
1084 | .supported = xpcs_2500basex_features, | |
1085 | .interface = xpcs_2500basex_interfaces, | |
1086 | .num_interfaces = ARRAY_SIZE(xpcs_2500basex_interfaces), | |
1087 | .an_mode = DW_2500BASEX, | |
1088 | .pma_config = nxp_sja1110_2500basex_pma_config, | |
1089 | }, | |
1090 | }; | |
1091 | ||
a54a8b71 VO |
1092 | static const struct xpcs_id xpcs_id_list[] = { |
1093 | { | |
1094 | .id = SYNOPSYS_XPCS_ID, | |
1095 | .mask = SYNOPSYS_XPCS_MASK, | |
1096 | .compat = synopsys_xpcs_compat, | |
dd0721ea VO |
1097 | }, { |
1098 | .id = NXP_SJA1105_XPCS_ID, | |
1099 | .mask = SYNOPSYS_XPCS_MASK, | |
1100 | .compat = nxp_sja1105_xpcs_compat, | |
f7380bba VO |
1101 | }, { |
1102 | .id = NXP_SJA1110_XPCS_ID, | |
1103 | .mask = SYNOPSYS_XPCS_MASK, | |
1104 | .compat = nxp_sja1110_xpcs_compat, | |
a54a8b71 VO |
1105 | }, |
1106 | }; | |
1107 | ||
11059740 VO |
1108 | static const struct phylink_pcs_ops xpcs_phylink_ops = { |
1109 | .pcs_config = xpcs_config, | |
1110 | .pcs_get_state = xpcs_get_state, | |
1111 | .pcs_link_up = xpcs_link_up, | |
1112 | }; | |
1113 | ||
5673ef86 VO |
1114 | struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, |
1115 | phy_interface_t interface) | |
fcb26bd2 | 1116 | { |
5673ef86 | 1117 | struct dw_xpcs *xpcs; |
2cac15da VO |
1118 | u32 xpcs_id; |
1119 | int i, ret; | |
1120 | ||
1121 | xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL); | |
1122 | if (!xpcs) | |
2cad5d2e | 1123 | return ERR_PTR(-ENOMEM); |
2cac15da VO |
1124 | |
1125 | xpcs->mdiodev = mdiodev; | |
1126 | ||
1127 | xpcs_id = xpcs_get_id(xpcs); | |
fcb26bd2 JA |
1128 | |
1129 | for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { | |
a54a8b71 | 1130 | const struct xpcs_id *entry = &xpcs_id_list[i]; |
9900074e | 1131 | const struct xpcs_compat *compat; |
fcb26bd2 | 1132 | |
9900074e VO |
1133 | if ((xpcs_id & entry->mask) != entry->id) |
1134 | continue; | |
fcb26bd2 | 1135 | |
9900074e VO |
1136 | xpcs->id = entry; |
1137 | ||
1138 | compat = xpcs_find_compat(entry, interface); | |
2cac15da VO |
1139 | if (!compat) { |
1140 | ret = -ENODEV; | |
1141 | goto out; | |
1142 | } | |
9900074e | 1143 | |
11059740 VO |
1144 | xpcs->pcs.ops = &xpcs_phylink_ops; |
1145 | xpcs->pcs.poll = true; | |
1146 | ||
2cac15da VO |
1147 | ret = xpcs_soft_reset(xpcs, compat); |
1148 | if (ret) | |
1149 | goto out; | |
1150 | ||
1151 | return xpcs; | |
fcb26bd2 JA |
1152 | } |
1153 | ||
2cac15da VO |
1154 | ret = -ENODEV; |
1155 | ||
1156 | out: | |
1157 | kfree(xpcs); | |
1158 | ||
1159 | return ERR_PTR(ret); | |
1160 | } | |
1161 | EXPORT_SYMBOL_GPL(xpcs_create); | |
1162 | ||
5673ef86 | 1163 | void xpcs_destroy(struct dw_xpcs *xpcs) |
2cac15da VO |
1164 | { |
1165 | kfree(xpcs); | |
fcb26bd2 | 1166 | } |
2cac15da | 1167 | EXPORT_SYMBOL_GPL(xpcs_destroy); |
fcb26bd2 | 1168 | |
fcb26bd2 | 1169 | MODULE_LICENSE("GPL v2"); |