1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Marvell 88x2222 dual-port multi-speed ethernet transceiver. |
4 | * |
5 | * Supports: |
6 | * XAUI on the host side. |
7 | * 1000Base-X or 10GBase-R on the line side. |
8 | * SGMII over 1000Base-X. |
9 | */ |
10 | #include <linux/module.h> |
11 | #include <linux/phy.h> |
12 | #include <linux/gpio.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/mdio.h> |
15 | #include <linux/marvell_phy.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_gpio.h> |
18 | #include <linux/sfp.h> |
19 | #include <linux/netdevice.h> |
20 | |
21 | /* Port PCS Configuration */ |
22 | #define MV_PCS_CONFIG 0xF002 |
23 | #define MV_PCS_HOST_XAUI 0x73 |
24 | #define MV_PCS_LINE_10GBR (0x71 << 8) |
25 | #define MV_PCS_LINE_1GBX_AN (0x7B << 8) |
26 | #define MV_PCS_LINE_SGMII_AN (0x7F << 8) |
27 | |
28 | /* Port Reset and Power Down */ |
29 | #define MV_PORT_RST 0xF003 |
30 | #define MV_LINE_RST_SW BIT(15) |
31 | #define MV_HOST_RST_SW BIT(7) |
32 | #define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW) |
33 | |
34 | /* PMD Receive Signal Detect */ |
35 | #define MV_RX_SIGNAL_DETECT 0x000A |
36 | #define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0) |
37 | |
38 | /* 1000Base-X/SGMII Control Register */ |
39 | #define MV_1GBX_CTRL (0x2000 + MII_BMCR) |
40 | |
41 | /* 1000BASE-X/SGMII Status Register */ |
42 | #define MV_1GBX_STAT (0x2000 + MII_BMSR) |
43 | |
44 | /* 1000Base-X Auto-Negotiation Advertisement Register */ |
45 | #define MV_1GBX_ADVERTISE (0x2000 + MII_ADVERTISE) |
46 | |
47 | /* 1000Base-X PHY Specific Status Register */ |
48 | #define MV_1GBX_PHY_STAT 0xA003 |
49 | #define MV_1GBX_PHY_STAT_AN_RESOLVED BIT(11) |
50 | #define MV_1GBX_PHY_STAT_DUPLEX BIT(13) |
51 | #define MV_1GBX_PHY_STAT_SPEED100 BIT(14) |
52 | #define MV_1GBX_PHY_STAT_SPEED1000 BIT(15) |
53 | |
54 | #define AUTONEG_TIMEOUT 3 |
55 | |
56 | struct mv2222_data { |
57 | phy_interface_t line_interface; |
58 | __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); |
59 | bool sfp_link; |
60 | }; |
61 | |
62 | /* SFI PMA transmit enable */ |
63 | static int mv2222_tx_enable(struct phy_device *phydev) |
64 | { |
65 | return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, |
66 | MDIO_PMD_TXDIS_GLOBAL); |
67 | } |
68 | |
69 | /* SFI PMA transmit disable */ |
70 | static int mv2222_tx_disable(struct phy_device *phydev) |
71 | { |
72 | return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, |
73 | MDIO_PMD_TXDIS_GLOBAL); |
74 | } |
75 | |
76 | static int mv2222_soft_reset(struct phy_device *phydev) |
77 | { |
78 | int val, ret; |
79 | |
80 | ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PORT_RST, |
81 | MV_PORT_RST_SW); |
82 | if (ret < 0) |
83 | return ret; |
84 | |
85 | return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND2, MV_PORT_RST, |
86 | val, !(val & MV_PORT_RST_SW), |
87 | 5000, 1000000, true); |
88 | } |
89 | |
90 | static int mv2222_disable_aneg(struct phy_device *phydev) |
91 | { |
92 | int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, |
93 | BMCR_ANENABLE | BMCR_ANRESTART); |
94 | if (ret < 0) |
95 | return ret; |
96 | |
97 | return mv2222_soft_reset(phydev); |
98 | } |
99 | |
100 | static int mv2222_enable_aneg(struct phy_device *phydev) |
101 | { |
102 | int ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL, |
103 | BMCR_ANENABLE | BMCR_RESET); |
104 | if (ret < 0) |
105 | return ret; |
106 | |
107 | return mv2222_soft_reset(phydev); |
108 | } |
109 | |
110 | static int mv2222_set_sgmii_speed(struct phy_device *phydev) |
111 | { |
112 | struct mv2222_data *priv = phydev->priv; |
113 | |
114 | switch (phydev->speed) { |
115 | default: |
116 | case SPEED_1000: |
117 | if ((linkmode_test_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
118 | addr: priv->supported) || |
119 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
120 | addr: priv->supported))) |
121 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
122 | MV_1GBX_CTRL, |
123 | BMCR_SPEED1000 | BMCR_SPEED100, |
124 | BMCR_SPEED1000); |
125 | |
126 | fallthrough; |
127 | case SPEED_100: |
128 | if ((linkmode_test_bit(nr: ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
129 | addr: priv->supported) || |
130 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_100baseT_Half_BIT, |
131 | addr: priv->supported))) |
132 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
133 | MV_1GBX_CTRL, |
134 | BMCR_SPEED1000 | BMCR_SPEED100, |
135 | BMCR_SPEED100); |
136 | fallthrough; |
137 | case SPEED_10: |
138 | if ((linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10baseT_Full_BIT, |
139 | addr: priv->supported) || |
140 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
141 | addr: priv->supported))) |
142 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
143 | MV_1GBX_CTRL, |
144 | BMCR_SPEED1000 | BMCR_SPEED100, |
145 | BMCR_SPEED10); |
146 | |
147 | return -EINVAL; |
148 | } |
149 | } |
150 | |
151 | static bool mv2222_is_10g_capable(struct phy_device *phydev) |
152 | { |
153 | struct mv2222_data *priv = phydev->priv; |
154 | |
155 | return (linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseT_Full_BIT, |
156 | addr: priv->supported) || |
157 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, |
158 | addr: priv->supported) || |
159 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, |
160 | addr: priv->supported) || |
161 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, |
162 | addr: priv->supported) || |
163 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, |
164 | addr: priv->supported) || |
165 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10000baseER_Full_BIT, |
166 | addr: priv->supported)); |
167 | } |
168 | |
169 | static bool mv2222_is_1gbx_capable(struct phy_device *phydev) |
170 | { |
171 | struct mv2222_data *priv = phydev->priv; |
172 | |
173 | return linkmode_test_bit(nr: ETHTOOL_LINK_MODE_1000baseX_Full_BIT, |
174 | addr: priv->supported); |
175 | } |
176 | |
177 | static bool mv2222_is_sgmii_capable(struct phy_device *phydev) |
178 | { |
179 | struct mv2222_data *priv = phydev->priv; |
180 | |
181 | return (linkmode_test_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
182 | addr: priv->supported) || |
183 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
184 | addr: priv->supported) || |
185 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
186 | addr: priv->supported) || |
187 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_100baseT_Half_BIT, |
188 | addr: priv->supported) || |
189 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10baseT_Full_BIT, |
190 | addr: priv->supported) || |
191 | linkmode_test_bit(nr: ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
192 | addr: priv->supported)); |
193 | } |
194 | |
195 | static int mv2222_config_line(struct phy_device *phydev) |
196 | { |
197 | struct mv2222_data *priv = phydev->priv; |
198 | |
199 | switch (priv->line_interface) { |
200 | case PHY_INTERFACE_MODE_10GBASER: |
201 | return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, |
202 | MV_PCS_HOST_XAUI | MV_PCS_LINE_10GBR); |
203 | case PHY_INTERFACE_MODE_1000BASEX: |
204 | return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, |
205 | MV_PCS_HOST_XAUI | MV_PCS_LINE_1GBX_AN); |
206 | case PHY_INTERFACE_MODE_SGMII: |
207 | return phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_PCS_CONFIG, |
208 | MV_PCS_HOST_XAUI | MV_PCS_LINE_SGMII_AN); |
209 | default: |
210 | return -EINVAL; |
211 | } |
212 | } |
213 | |
214 | /* Switch between 1G (1000Base-X/SGMII) and 10G (10GBase-R) modes */ |
215 | static int mv2222_swap_line_type(struct phy_device *phydev) |
216 | { |
217 | struct mv2222_data *priv = phydev->priv; |
218 | bool changed = false; |
219 | int ret; |
220 | |
221 | switch (priv->line_interface) { |
222 | case PHY_INTERFACE_MODE_10GBASER: |
223 | if (mv2222_is_1gbx_capable(phydev)) { |
224 | priv->line_interface = PHY_INTERFACE_MODE_1000BASEX; |
225 | changed = true; |
226 | } |
227 | |
228 | if (mv2222_is_sgmii_capable(phydev)) { |
229 | priv->line_interface = PHY_INTERFACE_MODE_SGMII; |
230 | changed = true; |
231 | } |
232 | |
233 | break; |
234 | case PHY_INTERFACE_MODE_1000BASEX: |
235 | case PHY_INTERFACE_MODE_SGMII: |
236 | if (mv2222_is_10g_capable(phydev)) { |
237 | priv->line_interface = PHY_INTERFACE_MODE_10GBASER; |
238 | changed = true; |
239 | } |
240 | |
241 | break; |
242 | default: |
243 | return -EINVAL; |
244 | } |
245 | |
246 | if (changed) { |
247 | ret = mv2222_config_line(phydev); |
248 | if (ret < 0) |
249 | return ret; |
250 | } |
251 | |
252 | return 0; |
253 | } |
254 | |
255 | static int mv2222_setup_forced(struct phy_device *phydev) |
256 | { |
257 | struct mv2222_data *priv = phydev->priv; |
258 | int ret; |
259 | |
260 | if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) { |
261 | if (phydev->speed < SPEED_10000 && |
262 | phydev->speed != SPEED_UNKNOWN) { |
263 | ret = mv2222_swap_line_type(phydev); |
264 | if (ret < 0) |
265 | return ret; |
266 | } |
267 | } |
268 | |
269 | if (priv->line_interface == PHY_INTERFACE_MODE_SGMII) { |
270 | ret = mv2222_set_sgmii_speed(phydev); |
271 | if (ret < 0) |
272 | return ret; |
273 | } |
274 | |
275 | return mv2222_disable_aneg(phydev); |
276 | } |
277 | |
278 | static int mv2222_config_aneg(struct phy_device *phydev) |
279 | { |
280 | struct mv2222_data *priv = phydev->priv; |
281 | int ret, adv; |
282 | |
283 | /* SFP is not present, do nothing */ |
284 | if (priv->line_interface == PHY_INTERFACE_MODE_NA) |
285 | return 0; |
286 | |
287 | if (phydev->autoneg == AUTONEG_DISABLE || |
288 | priv->line_interface == PHY_INTERFACE_MODE_10GBASER) |
289 | return mv2222_setup_forced(phydev); |
290 | |
291 | adv = linkmode_adv_to_mii_adv_x(linkmodes: priv->supported, |
292 | fd_bit: ETHTOOL_LINK_MODE_1000baseX_Full_BIT); |
293 | |
294 | ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_ADVERTISE, |
295 | ADVERTISE_1000XFULL | |
296 | ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM, |
297 | set: adv); |
298 | if (ret < 0) |
299 | return ret; |
300 | |
301 | return mv2222_enable_aneg(phydev); |
302 | } |
303 | |
304 | static int mv2222_aneg_done(struct phy_device *phydev) |
305 | { |
306 | int ret; |
307 | |
308 | if (mv2222_is_10g_capable(phydev)) { |
309 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); |
310 | if (ret < 0) |
311 | return ret; |
312 | |
313 | if (ret & MDIO_STAT1_LSTATUS) |
314 | return 1; |
315 | } |
316 | |
317 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); |
318 | if (ret < 0) |
319 | return ret; |
320 | |
321 | return (ret & BMSR_ANEGCOMPLETE); |
322 | } |
323 | |
324 | /* Returns negative on error, 0 if link is down, 1 if link is up */ |
325 | static int mv2222_read_status_10g(struct phy_device *phydev) |
326 | { |
327 | static int timeout; |
328 | int val, link = 0; |
329 | |
330 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); |
331 | if (val < 0) |
332 | return val; |
333 | |
334 | if (val & MDIO_STAT1_LSTATUS) { |
335 | link = 1; |
336 | |
337 | /* 10GBASE-R do not support auto-negotiation */ |
338 | phydev->autoneg = AUTONEG_DISABLE; |
339 | phydev->speed = SPEED_10000; |
340 | phydev->duplex = DUPLEX_FULL; |
341 | } else { |
342 | if (phydev->autoneg == AUTONEG_ENABLE) { |
343 | timeout++; |
344 | |
345 | if (timeout > AUTONEG_TIMEOUT) { |
346 | timeout = 0; |
347 | |
348 | val = mv2222_swap_line_type(phydev); |
349 | if (val < 0) |
350 | return val; |
351 | |
352 | return mv2222_config_aneg(phydev); |
353 | } |
354 | } |
355 | } |
356 | |
357 | return link; |
358 | } |
359 | |
360 | /* Returns negative on error, 0 if link is down, 1 if link is up */ |
361 | static int mv2222_read_status_1g(struct phy_device *phydev) |
362 | { |
363 | static int timeout; |
364 | int val, link = 0; |
365 | |
366 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); |
367 | if (val < 0) |
368 | return val; |
369 | |
370 | if (phydev->autoneg == AUTONEG_ENABLE && |
371 | !(val & BMSR_ANEGCOMPLETE)) { |
372 | timeout++; |
373 | |
374 | if (timeout > AUTONEG_TIMEOUT) { |
375 | timeout = 0; |
376 | |
377 | val = mv2222_swap_line_type(phydev); |
378 | if (val < 0) |
379 | return val; |
380 | |
381 | return mv2222_config_aneg(phydev); |
382 | } |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | if (!(val & BMSR_LSTATUS)) |
388 | return 0; |
389 | |
390 | link = 1; |
391 | |
392 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT); |
393 | if (val < 0) |
394 | return val; |
395 | |
396 | if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) { |
397 | if (val & MV_1GBX_PHY_STAT_DUPLEX) |
398 | phydev->duplex = DUPLEX_FULL; |
399 | else |
400 | phydev->duplex = DUPLEX_HALF; |
401 | |
402 | if (val & MV_1GBX_PHY_STAT_SPEED1000) |
403 | phydev->speed = SPEED_1000; |
404 | else if (val & MV_1GBX_PHY_STAT_SPEED100) |
405 | phydev->speed = SPEED_100; |
406 | else |
407 | phydev->speed = SPEED_10; |
408 | } |
409 | |
410 | return link; |
411 | } |
412 | |
413 | static bool mv2222_link_is_operational(struct phy_device *phydev) |
414 | { |
415 | struct mv2222_data *priv = phydev->priv; |
416 | int val; |
417 | |
418 | val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_RX_SIGNAL_DETECT); |
419 | if (val < 0 || !(val & MV_RX_SIGNAL_DETECT_GLOBAL)) |
420 | return false; |
421 | |
422 | if (phydev->sfp_bus && !priv->sfp_link) |
423 | return false; |
424 | |
425 | return true; |
426 | } |
427 | |
428 | static int mv2222_read_status(struct phy_device *phydev) |
429 | { |
430 | struct mv2222_data *priv = phydev->priv; |
431 | int link; |
432 | |
433 | phydev->link = 0; |
434 | phydev->speed = SPEED_UNKNOWN; |
435 | phydev->duplex = DUPLEX_UNKNOWN; |
436 | |
437 | if (!mv2222_link_is_operational(phydev)) |
438 | return 0; |
439 | |
440 | if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) |
441 | link = mv2222_read_status_10g(phydev); |
442 | else |
443 | link = mv2222_read_status_1g(phydev); |
444 | |
445 | if (link < 0) |
446 | return link; |
447 | |
448 | phydev->link = link; |
449 | |
450 | return 0; |
451 | } |
452 | |
453 | static int mv2222_resume(struct phy_device *phydev) |
454 | { |
455 | return mv2222_tx_enable(phydev); |
456 | } |
457 | |
458 | static int mv2222_suspend(struct phy_device *phydev) |
459 | { |
460 | return mv2222_tx_disable(phydev); |
461 | } |
462 | |
463 | static int mv2222_get_features(struct phy_device *phydev) |
464 | { |
465 | /* All supported linkmodes are set at probe */ |
466 | |
467 | return 0; |
468 | } |
469 | |
470 | static int mv2222_config_init(struct phy_device *phydev) |
471 | { |
472 | if (phydev->interface != PHY_INTERFACE_MODE_XAUI) |
473 | return -EINVAL; |
474 | |
475 | return 0; |
476 | } |
477 | |
478 | static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) |
479 | { |
480 | DECLARE_PHY_INTERFACE_MASK(interfaces); |
481 | struct phy_device *phydev = upstream; |
482 | phy_interface_t sfp_interface; |
483 | struct mv2222_data *priv; |
484 | struct device *dev; |
485 | int ret; |
486 | |
487 | __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_supported) = { 0, }; |
488 | |
489 | priv = phydev->priv; |
490 | dev = &phydev->mdio.dev; |
491 | |
492 | sfp_parse_support(bus: phydev->sfp_bus, id, support: sfp_supported, interfaces); |
493 | phydev->port = sfp_parse_port(bus: phydev->sfp_bus, id, support: sfp_supported); |
494 | sfp_interface = sfp_select_interface(bus: phydev->sfp_bus, link_modes: sfp_supported); |
495 | |
496 | dev_info(dev, "%s SFP module inserted\n" , phy_modes(sfp_interface)); |
497 | |
498 | if (sfp_interface != PHY_INTERFACE_MODE_10GBASER && |
499 | sfp_interface != PHY_INTERFACE_MODE_1000BASEX && |
500 | sfp_interface != PHY_INTERFACE_MODE_SGMII) { |
501 | dev_err(dev, "Incompatible SFP module inserted\n" ); |
502 | |
503 | return -EINVAL; |
504 | } |
505 | |
506 | priv->line_interface = sfp_interface; |
507 | linkmode_and(dst: priv->supported, a: phydev->supported, b: sfp_supported); |
508 | |
509 | ret = mv2222_config_line(phydev); |
510 | if (ret < 0) |
511 | return ret; |
512 | |
513 | if (mutex_trylock(lock: &phydev->lock)) { |
514 | ret = mv2222_config_aneg(phydev); |
515 | mutex_unlock(lock: &phydev->lock); |
516 | } |
517 | |
518 | return ret; |
519 | } |
520 | |
521 | static void mv2222_sfp_remove(void *upstream) |
522 | { |
523 | struct phy_device *phydev = upstream; |
524 | struct mv2222_data *priv; |
525 | |
526 | priv = phydev->priv; |
527 | |
528 | priv->line_interface = PHY_INTERFACE_MODE_NA; |
529 | linkmode_zero(dst: priv->supported); |
530 | phydev->port = PORT_NONE; |
531 | } |
532 | |
533 | static void mv2222_sfp_link_up(void *upstream) |
534 | { |
535 | struct phy_device *phydev = upstream; |
536 | struct mv2222_data *priv; |
537 | |
538 | priv = phydev->priv; |
539 | priv->sfp_link = true; |
540 | } |
541 | |
542 | static void mv2222_sfp_link_down(void *upstream) |
543 | { |
544 | struct phy_device *phydev = upstream; |
545 | struct mv2222_data *priv; |
546 | |
547 | priv = phydev->priv; |
548 | priv->sfp_link = false; |
549 | } |
550 | |
551 | static const struct sfp_upstream_ops sfp_phy_ops = { |
552 | .module_insert = mv2222_sfp_insert, |
553 | .module_remove = mv2222_sfp_remove, |
554 | .link_up = mv2222_sfp_link_up, |
555 | .link_down = mv2222_sfp_link_down, |
556 | .attach = phy_sfp_attach, |
557 | .detach = phy_sfp_detach, |
558 | }; |
559 | |
560 | static int mv2222_probe(struct phy_device *phydev) |
561 | { |
562 | struct device *dev = &phydev->mdio.dev; |
563 | struct mv2222_data *priv = NULL; |
564 | |
565 | __ETHTOOL_DECLARE_LINK_MODE_MASK(supported) = { 0, }; |
566 | |
567 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_Autoneg_BIT, addr: supported); |
568 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_Pause_BIT, addr: supported); |
569 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_Asym_Pause_BIT, addr: supported); |
570 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_FIBRE_BIT, addr: supported); |
571 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_TP_BIT, addr: supported); |
572 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10baseT_Half_BIT, addr: supported); |
573 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10baseT_Full_BIT, addr: supported); |
574 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseT_Half_BIT, addr: supported); |
575 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseT_Full_BIT, addr: supported); |
576 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Half_BIT, addr: supported); |
577 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Full_BIT, addr: supported); |
578 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseX_Full_BIT, addr: supported); |
579 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseT_Full_BIT, addr: supported); |
580 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, addr: supported); |
581 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, addr: supported); |
582 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, addr: supported); |
583 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, addr: supported); |
584 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10000baseER_Full_BIT, addr: supported); |
585 | |
586 | linkmode_copy(dst: phydev->supported, src: supported); |
587 | |
588 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
589 | if (!priv) |
590 | return -ENOMEM; |
591 | |
592 | priv->line_interface = PHY_INTERFACE_MODE_NA; |
593 | phydev->priv = priv; |
594 | |
595 | return phy_sfp_probe(phydev, ops: &sfp_phy_ops); |
596 | } |
597 | |
598 | static struct phy_driver mv2222_drivers[] = { |
599 | { |
600 | .phy_id = MARVELL_PHY_ID_88X2222, |
601 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
602 | .name = "Marvell 88X2222" , |
603 | .get_features = mv2222_get_features, |
604 | .soft_reset = mv2222_soft_reset, |
605 | .config_init = mv2222_config_init, |
606 | .config_aneg = mv2222_config_aneg, |
607 | .aneg_done = mv2222_aneg_done, |
608 | .probe = mv2222_probe, |
609 | .suspend = mv2222_suspend, |
610 | .resume = mv2222_resume, |
611 | .read_status = mv2222_read_status, |
612 | }, |
613 | }; |
614 | module_phy_driver(mv2222_drivers); |
615 | |
616 | static struct mdio_device_id __maybe_unused mv2222_tbl[] = { |
617 | { MARVELL_PHY_ID_88X2222, MARVELL_PHY_ID_MASK }, |
618 | { } |
619 | }; |
620 | MODULE_DEVICE_TABLE(mdio, mv2222_tbl); |
621 | |
622 | MODULE_DESCRIPTION("Marvell 88x2222 ethernet transceiver driver" ); |
623 | MODULE_LICENSE("GPL" ); |
624 | |