1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (C) 2021 Maxlinear Corporation
3 * Copyright (C) 2020 Intel Corporation
4 *
5 * Drivers for Maxlinear Ethernet GPY
6 *
7 */
8
9 #include <linux/module.h>
10 #include <linux/bitfield.h>
11 #include <linux/phy.h>
12 #include <linux/netdevice.h>
13
14 /* PHY ID */
15 #define PHY_ID_GPYx15B_MASK 0xFFFFFFFC
16 #define PHY_ID_GPY21xB_MASK 0xFFFFFFF9
17 #define PHY_ID_GPY2xx 0x67C9DC00
18 #define PHY_ID_GPY115B 0x67C9DF00
19 #define PHY_ID_GPY115C 0x67C9DF10
20 #define PHY_ID_GPY211B 0x67C9DE08
21 #define PHY_ID_GPY211C 0x67C9DE10
22 #define PHY_ID_GPY212B 0x67C9DE09
23 #define PHY_ID_GPY212C 0x67C9DE20
24 #define PHY_ID_GPY215B 0x67C9DF04
25 #define PHY_ID_GPY215C 0x67C9DF20
26 #define PHY_ID_GPY241B 0x67C9DE40
27 #define PHY_ID_GPY241BM 0x67C9DE80
28 #define PHY_ID_GPY245B 0x67C9DEC0
29
30 #define PHY_MIISTAT 0x18 /* MII state */
31 #define PHY_IMASK 0x19 /* interrupt mask */
32 #define PHY_ISTAT 0x1A /* interrupt status */
33 #define PHY_FWV 0x1E /* firmware version */
34
35 #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
36 #define PHY_MIISTAT_DPX BIT(3)
37 #define PHY_MIISTAT_LS BIT(10)
38
39 #define PHY_MIISTAT_SPD_10 0
40 #define PHY_MIISTAT_SPD_100 1
41 #define PHY_MIISTAT_SPD_1000 2
42 #define PHY_MIISTAT_SPD_2500 4
43
44 #define PHY_IMASK_WOL BIT(15) /* Wake-on-LAN */
45 #define PHY_IMASK_ANC BIT(10) /* Auto-Neg complete */
46 #define PHY_IMASK_ADSC BIT(5) /* Link auto-downspeed detect */
47 #define PHY_IMASK_DXMC BIT(2) /* Duplex mode change */
48 #define PHY_IMASK_LSPC BIT(1) /* Link speed change */
49 #define PHY_IMASK_LSTC BIT(0) /* Link state change */
50 #define PHY_IMASK_MASK (PHY_IMASK_LSTC | \
51 PHY_IMASK_LSPC | \
52 PHY_IMASK_DXMC | \
53 PHY_IMASK_ADSC | \
54 PHY_IMASK_ANC)
55
56 #define PHY_FWV_REL_MASK BIT(15)
57 #define PHY_FWV_TYPE_MASK GENMASK(11, 8)
58 #define PHY_FWV_MINOR_MASK GENMASK(7, 0)
59
60 /* SGMII */
61 #define VSPEC1_SGMII_CTRL 0x08
62 #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
63 #define VSPEC1_SGMII_CTRL_ANRS BIT(9) /* Restart Aneg */
64 #define VSPEC1_SGMII_ANEN_ANRS (VSPEC1_SGMII_CTRL_ANEN | \
65 VSPEC1_SGMII_CTRL_ANRS)
66
67 /* WoL */
68 #define VPSPEC2_WOL_CTL 0x0E06
69 #define VPSPEC2_WOL_AD01 0x0E08
70 #define VPSPEC2_WOL_AD23 0x0E09
71 #define VPSPEC2_WOL_AD45 0x0E0A
72 #define WOL_EN BIT(0)
73
74 static const struct {
75 int type;
76 int minor;
77 } ver_need_sgmii_reaneg[] = {
78 {7, 0x6D},
79 {8, 0x6D},
80 {9, 0x73},
81 };
82
gpy_config_init(struct phy_device * phydev)83 static int gpy_config_init(struct phy_device *phydev)
84 {
85 int ret;
86
87 /* Mask all interrupts */
88 ret = phy_write(phydev, PHY_IMASK, 0);
89 if (ret)
90 return ret;
91
92 /* Clear all pending interrupts */
93 ret = phy_read(phydev, PHY_ISTAT);
94 return ret < 0 ? ret : 0;
95 }
96
gpy_probe(struct phy_device * phydev)97 static int gpy_probe(struct phy_device *phydev)
98 {
99 int ret;
100
101 if (!phydev->is_c45) {
102 ret = phy_get_c45_ids(phydev);
103 if (ret < 0)
104 return ret;
105 }
106
107 /* Show GPY PHY FW version in dmesg */
108 ret = phy_read(phydev, PHY_FWV);
109 if (ret < 0)
110 return ret;
111
112 phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
113 (ret & PHY_FWV_REL_MASK) ? "release" : "test");
114
115 return 0;
116 }
117
gpy_sgmii_need_reaneg(struct phy_device * phydev)118 static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
119 {
120 int fw_ver, fw_type, fw_minor;
121 size_t i;
122
123 fw_ver = phy_read(phydev, PHY_FWV);
124 if (fw_ver < 0)
125 return true;
126
127 fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
128 fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
129
130 for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
131 if (fw_type != ver_need_sgmii_reaneg[i].type)
132 continue;
133 if (fw_minor < ver_need_sgmii_reaneg[i].minor)
134 return true;
135 break;
136 }
137
138 return false;
139 }
140
gpy_2500basex_chk(struct phy_device * phydev)141 static bool gpy_2500basex_chk(struct phy_device *phydev)
142 {
143 int ret;
144
145 ret = phy_read(phydev, PHY_MIISTAT);
146 if (ret < 0) {
147 phydev_err(phydev, "Error: MDIO register access failed: %d\n",
148 ret);
149 return false;
150 }
151
152 if (!(ret & PHY_MIISTAT_LS) ||
153 FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
154 return false;
155
156 phydev->speed = SPEED_2500;
157 phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
158 phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
159 VSPEC1_SGMII_CTRL_ANEN, 0);
160 return true;
161 }
162
gpy_sgmii_aneg_en(struct phy_device * phydev)163 static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
164 {
165 int ret;
166
167 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
168 if (ret < 0) {
169 phydev_err(phydev, "Error: MMD register access failed: %d\n",
170 ret);
171 return true;
172 }
173
174 return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
175 }
176
gpy_config_aneg(struct phy_device * phydev)177 static int gpy_config_aneg(struct phy_device *phydev)
178 {
179 bool changed = false;
180 u32 adv;
181 int ret;
182
183 if (phydev->autoneg == AUTONEG_DISABLE) {
184 /* Configure half duplex with genphy_setup_forced,
185 * because genphy_c45_pma_setup_forced does not support.
186 */
187 return phydev->duplex != DUPLEX_FULL
188 ? genphy_setup_forced(phydev)
189 : genphy_c45_pma_setup_forced(phydev);
190 }
191
192 ret = genphy_c45_an_config_aneg(phydev);
193 if (ret < 0)
194 return ret;
195 if (ret > 0)
196 changed = true;
197
198 adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
199 ret = phy_modify_changed(phydev, MII_CTRL1000,
200 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
201 adv);
202 if (ret < 0)
203 return ret;
204 if (ret > 0)
205 changed = true;
206
207 ret = genphy_c45_check_and_restart_aneg(phydev, changed);
208 if (ret < 0)
209 return ret;
210
211 if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
212 phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
213 return 0;
214
215 /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
216 * disabled.
217 */
218 if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
219 !gpy_sgmii_aneg_en(phydev))
220 return 0;
221
222 /* There is a design constraint in GPY2xx device where SGMII AN is
223 * only triggered when there is change of speed. If, PHY link
224 * partner`s speed is still same even after PHY TPI is down and up
225 * again, SGMII AN is not triggered and hence no new in-band message
226 * from GPY to MAC side SGMII.
227 * This could cause an issue during power up, when PHY is up prior to
228 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
229 * wouldn`t receive new in-band message from GPY with correct link
230 * status, speed and duplex info.
231 *
232 * 1) If PHY is already up and TPI link status is still down (such as
233 * hard reboot), TPI link status is polled for 4 seconds before
234 * retriggerring SGMII AN.
235 * 2) If PHY is already up and TPI link status is also up (such as soft
236 * reboot), polling of TPI link status is not needed and SGMII AN is
237 * immediately retriggered.
238 * 3) Other conditions such as PHY is down, speed change etc, skip
239 * retriggering SGMII AN. Note: in case of speed change, GPY FW will
240 * initiate SGMII AN.
241 */
242
243 if (phydev->state != PHY_UP)
244 return 0;
245
246 ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
247 20000, 4000000, false);
248 if (ret == -ETIMEDOUT)
249 return 0;
250 else if (ret < 0)
251 return ret;
252
253 /* Trigger SGMII AN. */
254 return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
255 VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
256 }
257
gpy_update_interface(struct phy_device * phydev)258 static void gpy_update_interface(struct phy_device *phydev)
259 {
260 int ret;
261
262 /* Interface mode is fixed for USXGMII and integrated PHY */
263 if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
264 phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
265 return;
266
267 /* Automatically switch SERDES interface between SGMII and 2500-BaseX
268 * according to speed. Disable ANEG in 2500-BaseX mode.
269 */
270 switch (phydev->speed) {
271 case SPEED_2500:
272 phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
273 ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
274 VSPEC1_SGMII_CTRL_ANEN, 0);
275 if (ret < 0)
276 phydev_err(phydev,
277 "Error: Disable of SGMII ANEG failed: %d\n",
278 ret);
279 break;
280 case SPEED_1000:
281 case SPEED_100:
282 case SPEED_10:
283 phydev->interface = PHY_INTERFACE_MODE_SGMII;
284 if (gpy_sgmii_aneg_en(phydev))
285 break;
286 /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
287 * if ANEG is disabled (in 2500-BaseX mode).
288 */
289 ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
290 VSPEC1_SGMII_ANEN_ANRS,
291 VSPEC1_SGMII_ANEN_ANRS);
292 if (ret < 0)
293 phydev_err(phydev,
294 "Error: Enable of SGMII ANEG failed: %d\n",
295 ret);
296 break;
297 }
298 }
299
gpy_read_status(struct phy_device * phydev)300 static int gpy_read_status(struct phy_device *phydev)
301 {
302 int ret;
303
304 ret = genphy_update_link(phydev);
305 if (ret)
306 return ret;
307
308 phydev->speed = SPEED_UNKNOWN;
309 phydev->duplex = DUPLEX_UNKNOWN;
310 phydev->pause = 0;
311 phydev->asym_pause = 0;
312
313 if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
314 ret = genphy_c45_read_lpa(phydev);
315 if (ret < 0)
316 return ret;
317
318 /* Read the link partner's 1G advertisement */
319 ret = phy_read(phydev, MII_STAT1000);
320 if (ret < 0)
321 return ret;
322 mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
323 } else if (phydev->autoneg == AUTONEG_DISABLE) {
324 linkmode_zero(phydev->lp_advertising);
325 }
326
327 ret = phy_read(phydev, PHY_MIISTAT);
328 if (ret < 0)
329 return ret;
330
331 phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
332 phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
333 switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
334 case PHY_MIISTAT_SPD_10:
335 phydev->speed = SPEED_10;
336 break;
337 case PHY_MIISTAT_SPD_100:
338 phydev->speed = SPEED_100;
339 break;
340 case PHY_MIISTAT_SPD_1000:
341 phydev->speed = SPEED_1000;
342 break;
343 case PHY_MIISTAT_SPD_2500:
344 phydev->speed = SPEED_2500;
345 break;
346 }
347
348 if (phydev->link)
349 gpy_update_interface(phydev);
350
351 return 0;
352 }
353
gpy_config_intr(struct phy_device * phydev)354 static int gpy_config_intr(struct phy_device *phydev)
355 {
356 u16 mask = 0;
357
358 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
359 mask = PHY_IMASK_MASK;
360
361 return phy_write(phydev, PHY_IMASK, mask);
362 }
363
gpy_handle_interrupt(struct phy_device * phydev)364 static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
365 {
366 int reg;
367
368 reg = phy_read(phydev, PHY_ISTAT);
369 if (reg < 0) {
370 phy_error(phydev);
371 return IRQ_NONE;
372 }
373
374 if (!(reg & PHY_IMASK_MASK))
375 return IRQ_NONE;
376
377 phy_trigger_machine(phydev);
378
379 return IRQ_HANDLED;
380 }
381
gpy_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)382 static int gpy_set_wol(struct phy_device *phydev,
383 struct ethtool_wolinfo *wol)
384 {
385 struct net_device *attach_dev = phydev->attached_dev;
386 int ret;
387
388 if (wol->wolopts & WAKE_MAGIC) {
389 /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
390 * VPSPEC2_WOL_AD45 = Byte0:Byte1
391 * VPSPEC2_WOL_AD23 = Byte2:Byte3
392 * VPSPEC2_WOL_AD01 = Byte4:Byte5
393 */
394 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
395 VPSPEC2_WOL_AD45,
396 ((attach_dev->dev_addr[0] << 8) |
397 attach_dev->dev_addr[1]));
398 if (ret < 0)
399 return ret;
400
401 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
402 VPSPEC2_WOL_AD23,
403 ((attach_dev->dev_addr[2] << 8) |
404 attach_dev->dev_addr[3]));
405 if (ret < 0)
406 return ret;
407
408 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
409 VPSPEC2_WOL_AD01,
410 ((attach_dev->dev_addr[4] << 8) |
411 attach_dev->dev_addr[5]));
412 if (ret < 0)
413 return ret;
414
415 /* Enable the WOL interrupt */
416 ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
417 if (ret < 0)
418 return ret;
419
420 /* Enable magic packet matching */
421 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
422 VPSPEC2_WOL_CTL,
423 WOL_EN);
424 if (ret < 0)
425 return ret;
426
427 /* Clear the interrupt status register.
428 * Only WoL is enabled so clear all.
429 */
430 ret = phy_read(phydev, PHY_ISTAT);
431 if (ret < 0)
432 return ret;
433 } else {
434 /* Disable magic packet matching */
435 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
436 VPSPEC2_WOL_CTL,
437 WOL_EN);
438 if (ret < 0)
439 return ret;
440 }
441
442 if (wol->wolopts & WAKE_PHY) {
443 /* Enable the link state change interrupt */
444 ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
445 if (ret < 0)
446 return ret;
447
448 /* Clear the interrupt status register */
449 ret = phy_read(phydev, PHY_ISTAT);
450 if (ret < 0)
451 return ret;
452
453 if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
454 phy_trigger_machine(phydev);
455
456 return 0;
457 }
458
459 /* Disable the link state change interrupt */
460 return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
461 }
462
gpy_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)463 static void gpy_get_wol(struct phy_device *phydev,
464 struct ethtool_wolinfo *wol)
465 {
466 int ret;
467
468 wol->supported = WAKE_MAGIC | WAKE_PHY;
469 wol->wolopts = 0;
470
471 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
472 if (ret & WOL_EN)
473 wol->wolopts |= WAKE_MAGIC;
474
475 ret = phy_read(phydev, PHY_IMASK);
476 if (ret & PHY_IMASK_LSTC)
477 wol->wolopts |= WAKE_PHY;
478 }
479
gpy_loopback(struct phy_device * phydev,bool enable)480 static int gpy_loopback(struct phy_device *phydev, bool enable)
481 {
482 int ret;
483
484 ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
485 enable ? BMCR_LOOPBACK : 0);
486 if (!ret) {
487 /* It takes some time for PHY device to switch
488 * into/out-of loopback mode.
489 */
490 msleep(100);
491 }
492
493 return ret;
494 }
495
gpy115_loopback(struct phy_device * phydev,bool enable)496 static int gpy115_loopback(struct phy_device *phydev, bool enable)
497 {
498 int ret;
499 int fw_minor;
500
501 if (enable)
502 return gpy_loopback(phydev, enable);
503
504 ret = phy_read(phydev, PHY_FWV);
505 if (ret < 0)
506 return ret;
507
508 fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, ret);
509 if (fw_minor > 0x0076)
510 return gpy_loopback(phydev, 0);
511
512 return genphy_soft_reset(phydev);
513 }
514
515 static struct phy_driver gpy_drivers[] = {
516 {
517 PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
518 .name = "Maxlinear Ethernet GPY2xx",
519 .get_features = genphy_c45_pma_read_abilities,
520 .config_init = gpy_config_init,
521 .probe = gpy_probe,
522 .suspend = genphy_suspend,
523 .resume = genphy_resume,
524 .config_aneg = gpy_config_aneg,
525 .aneg_done = genphy_c45_aneg_done,
526 .read_status = gpy_read_status,
527 .config_intr = gpy_config_intr,
528 .handle_interrupt = gpy_handle_interrupt,
529 .set_wol = gpy_set_wol,
530 .get_wol = gpy_get_wol,
531 .set_loopback = gpy_loopback,
532 },
533 {
534 .phy_id = PHY_ID_GPY115B,
535 .phy_id_mask = PHY_ID_GPYx15B_MASK,
536 .name = "Maxlinear Ethernet GPY115B",
537 .get_features = genphy_c45_pma_read_abilities,
538 .config_init = gpy_config_init,
539 .probe = gpy_probe,
540 .suspend = genphy_suspend,
541 .resume = genphy_resume,
542 .config_aneg = gpy_config_aneg,
543 .aneg_done = genphy_c45_aneg_done,
544 .read_status = gpy_read_status,
545 .config_intr = gpy_config_intr,
546 .handle_interrupt = gpy_handle_interrupt,
547 .set_wol = gpy_set_wol,
548 .get_wol = gpy_get_wol,
549 .set_loopback = gpy115_loopback,
550 },
551 {
552 PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
553 .name = "Maxlinear Ethernet GPY115C",
554 .get_features = genphy_c45_pma_read_abilities,
555 .config_init = gpy_config_init,
556 .probe = gpy_probe,
557 .suspend = genphy_suspend,
558 .resume = genphy_resume,
559 .config_aneg = gpy_config_aneg,
560 .aneg_done = genphy_c45_aneg_done,
561 .read_status = gpy_read_status,
562 .config_intr = gpy_config_intr,
563 .handle_interrupt = gpy_handle_interrupt,
564 .set_wol = gpy_set_wol,
565 .get_wol = gpy_get_wol,
566 .set_loopback = gpy115_loopback,
567 },
568 {
569 .phy_id = PHY_ID_GPY211B,
570 .phy_id_mask = PHY_ID_GPY21xB_MASK,
571 .name = "Maxlinear Ethernet GPY211B",
572 .get_features = genphy_c45_pma_read_abilities,
573 .config_init = gpy_config_init,
574 .probe = gpy_probe,
575 .suspend = genphy_suspend,
576 .resume = genphy_resume,
577 .config_aneg = gpy_config_aneg,
578 .aneg_done = genphy_c45_aneg_done,
579 .read_status = gpy_read_status,
580 .config_intr = gpy_config_intr,
581 .handle_interrupt = gpy_handle_interrupt,
582 .set_wol = gpy_set_wol,
583 .get_wol = gpy_get_wol,
584 .set_loopback = gpy_loopback,
585 },
586 {
587 PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
588 .name = "Maxlinear Ethernet GPY211C",
589 .get_features = genphy_c45_pma_read_abilities,
590 .config_init = gpy_config_init,
591 .probe = gpy_probe,
592 .suspend = genphy_suspend,
593 .resume = genphy_resume,
594 .config_aneg = gpy_config_aneg,
595 .aneg_done = genphy_c45_aneg_done,
596 .read_status = gpy_read_status,
597 .config_intr = gpy_config_intr,
598 .handle_interrupt = gpy_handle_interrupt,
599 .set_wol = gpy_set_wol,
600 .get_wol = gpy_get_wol,
601 .set_loopback = gpy_loopback,
602 },
603 {
604 .phy_id = PHY_ID_GPY212B,
605 .phy_id_mask = PHY_ID_GPY21xB_MASK,
606 .name = "Maxlinear Ethernet GPY212B",
607 .get_features = genphy_c45_pma_read_abilities,
608 .config_init = gpy_config_init,
609 .probe = gpy_probe,
610 .suspend = genphy_suspend,
611 .resume = genphy_resume,
612 .config_aneg = gpy_config_aneg,
613 .aneg_done = genphy_c45_aneg_done,
614 .read_status = gpy_read_status,
615 .config_intr = gpy_config_intr,
616 .handle_interrupt = gpy_handle_interrupt,
617 .set_wol = gpy_set_wol,
618 .get_wol = gpy_get_wol,
619 .set_loopback = gpy_loopback,
620 },
621 {
622 PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
623 .name = "Maxlinear Ethernet GPY212C",
624 .get_features = genphy_c45_pma_read_abilities,
625 .config_init = gpy_config_init,
626 .probe = gpy_probe,
627 .suspend = genphy_suspend,
628 .resume = genphy_resume,
629 .config_aneg = gpy_config_aneg,
630 .aneg_done = genphy_c45_aneg_done,
631 .read_status = gpy_read_status,
632 .config_intr = gpy_config_intr,
633 .handle_interrupt = gpy_handle_interrupt,
634 .set_wol = gpy_set_wol,
635 .get_wol = gpy_get_wol,
636 .set_loopback = gpy_loopback,
637 },
638 {
639 .phy_id = PHY_ID_GPY215B,
640 .phy_id_mask = PHY_ID_GPYx15B_MASK,
641 .name = "Maxlinear Ethernet GPY215B",
642 .get_features = genphy_c45_pma_read_abilities,
643 .config_init = gpy_config_init,
644 .probe = gpy_probe,
645 .suspend = genphy_suspend,
646 .resume = genphy_resume,
647 .config_aneg = gpy_config_aneg,
648 .aneg_done = genphy_c45_aneg_done,
649 .read_status = gpy_read_status,
650 .config_intr = gpy_config_intr,
651 .handle_interrupt = gpy_handle_interrupt,
652 .set_wol = gpy_set_wol,
653 .get_wol = gpy_get_wol,
654 .set_loopback = gpy_loopback,
655 },
656 {
657 PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
658 .name = "Maxlinear Ethernet GPY215C",
659 .get_features = genphy_c45_pma_read_abilities,
660 .config_init = gpy_config_init,
661 .probe = gpy_probe,
662 .suspend = genphy_suspend,
663 .resume = genphy_resume,
664 .config_aneg = gpy_config_aneg,
665 .aneg_done = genphy_c45_aneg_done,
666 .read_status = gpy_read_status,
667 .config_intr = gpy_config_intr,
668 .handle_interrupt = gpy_handle_interrupt,
669 .set_wol = gpy_set_wol,
670 .get_wol = gpy_get_wol,
671 .set_loopback = gpy_loopback,
672 },
673 {
674 PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
675 .name = "Maxlinear Ethernet GPY241B",
676 .get_features = genphy_c45_pma_read_abilities,
677 .config_init = gpy_config_init,
678 .probe = gpy_probe,
679 .suspend = genphy_suspend,
680 .resume = genphy_resume,
681 .config_aneg = gpy_config_aneg,
682 .aneg_done = genphy_c45_aneg_done,
683 .read_status = gpy_read_status,
684 .config_intr = gpy_config_intr,
685 .handle_interrupt = gpy_handle_interrupt,
686 .set_wol = gpy_set_wol,
687 .get_wol = gpy_get_wol,
688 .set_loopback = gpy_loopback,
689 },
690 {
691 PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
692 .name = "Maxlinear Ethernet GPY241BM",
693 .get_features = genphy_c45_pma_read_abilities,
694 .config_init = gpy_config_init,
695 .probe = gpy_probe,
696 .suspend = genphy_suspend,
697 .resume = genphy_resume,
698 .config_aneg = gpy_config_aneg,
699 .aneg_done = genphy_c45_aneg_done,
700 .read_status = gpy_read_status,
701 .config_intr = gpy_config_intr,
702 .handle_interrupt = gpy_handle_interrupt,
703 .set_wol = gpy_set_wol,
704 .get_wol = gpy_get_wol,
705 .set_loopback = gpy_loopback,
706 },
707 {
708 PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
709 .name = "Maxlinear Ethernet GPY245B",
710 .get_features = genphy_c45_pma_read_abilities,
711 .config_init = gpy_config_init,
712 .probe = gpy_probe,
713 .suspend = genphy_suspend,
714 .resume = genphy_resume,
715 .config_aneg = gpy_config_aneg,
716 .aneg_done = genphy_c45_aneg_done,
717 .read_status = gpy_read_status,
718 .config_intr = gpy_config_intr,
719 .handle_interrupt = gpy_handle_interrupt,
720 .set_wol = gpy_set_wol,
721 .get_wol = gpy_get_wol,
722 .set_loopback = gpy_loopback,
723 },
724 };
725 module_phy_driver(gpy_drivers);
726
727 static struct mdio_device_id __maybe_unused gpy_tbl[] = {
728 {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
729 {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
730 {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
731 {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
732 {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
733 {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
734 {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
735 {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
736 {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
737 {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
738 {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
739 {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
740 { }
741 };
742 MODULE_DEVICE_TABLE(mdio, gpy_tbl);
743
744 MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
745 MODULE_AUTHOR("Xu Liang");
746 MODULE_LICENSE("GPL");
747