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