Merge tag 'pci-v5.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / net / phy / mxl-gpy.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
496 static struct phy_driver gpy_drivers[] = {
497         {
498                 PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
499                 .name           = "Maxlinear Ethernet GPY2xx",
500                 .get_features   = genphy_c45_pma_read_abilities,
501                 .config_init    = gpy_config_init,
502                 .probe          = gpy_probe,
503                 .suspend        = genphy_suspend,
504                 .resume         = genphy_resume,
505                 .config_aneg    = gpy_config_aneg,
506                 .aneg_done      = genphy_c45_aneg_done,
507                 .read_status    = gpy_read_status,
508                 .config_intr    = gpy_config_intr,
509                 .handle_interrupt = gpy_handle_interrupt,
510                 .set_wol        = gpy_set_wol,
511                 .get_wol        = gpy_get_wol,
512                 .set_loopback   = gpy_loopback,
513         },
514         {
515                 .phy_id         = PHY_ID_GPY115B,
516                 .phy_id_mask    = PHY_ID_GPYx15B_MASK,
517                 .name           = "Maxlinear Ethernet GPY115B",
518                 .get_features   = genphy_c45_pma_read_abilities,
519                 .config_init    = gpy_config_init,
520                 .probe          = gpy_probe,
521                 .suspend        = genphy_suspend,
522                 .resume         = genphy_resume,
523                 .config_aneg    = gpy_config_aneg,
524                 .aneg_done      = genphy_c45_aneg_done,
525                 .read_status    = gpy_read_status,
526                 .config_intr    = gpy_config_intr,
527                 .handle_interrupt = gpy_handle_interrupt,
528                 .set_wol        = gpy_set_wol,
529                 .get_wol        = gpy_get_wol,
530                 .set_loopback   = gpy_loopback,
531         },
532         {
533                 PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
534                 .name           = "Maxlinear Ethernet GPY115C",
535                 .get_features   = genphy_c45_pma_read_abilities,
536                 .config_init    = gpy_config_init,
537                 .probe          = gpy_probe,
538                 .suspend        = genphy_suspend,
539                 .resume         = genphy_resume,
540                 .config_aneg    = gpy_config_aneg,
541                 .aneg_done      = genphy_c45_aneg_done,
542                 .read_status    = gpy_read_status,
543                 .config_intr    = gpy_config_intr,
544                 .handle_interrupt = gpy_handle_interrupt,
545                 .set_wol        = gpy_set_wol,
546                 .get_wol        = gpy_get_wol,
547                 .set_loopback   = gpy_loopback,
548         },
549         {
550                 .phy_id         = PHY_ID_GPY211B,
551                 .phy_id_mask    = PHY_ID_GPY21xB_MASK,
552                 .name           = "Maxlinear Ethernet GPY211B",
553                 .get_features   = genphy_c45_pma_read_abilities,
554                 .config_init    = gpy_config_init,
555                 .probe          = gpy_probe,
556                 .suspend        = genphy_suspend,
557                 .resume         = genphy_resume,
558                 .config_aneg    = gpy_config_aneg,
559                 .aneg_done      = genphy_c45_aneg_done,
560                 .read_status    = gpy_read_status,
561                 .config_intr    = gpy_config_intr,
562                 .handle_interrupt = gpy_handle_interrupt,
563                 .set_wol        = gpy_set_wol,
564                 .get_wol        = gpy_get_wol,
565                 .set_loopback   = gpy_loopback,
566         },
567         {
568                 PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
569                 .name           = "Maxlinear Ethernet GPY211C",
570                 .get_features   = genphy_c45_pma_read_abilities,
571                 .config_init    = gpy_config_init,
572                 .probe          = gpy_probe,
573                 .suspend        = genphy_suspend,
574                 .resume         = genphy_resume,
575                 .config_aneg    = gpy_config_aneg,
576                 .aneg_done      = genphy_c45_aneg_done,
577                 .read_status    = gpy_read_status,
578                 .config_intr    = gpy_config_intr,
579                 .handle_interrupt = gpy_handle_interrupt,
580                 .set_wol        = gpy_set_wol,
581                 .get_wol        = gpy_get_wol,
582                 .set_loopback   = gpy_loopback,
583         },
584         {
585                 .phy_id         = PHY_ID_GPY212B,
586                 .phy_id_mask    = PHY_ID_GPY21xB_MASK,
587                 .name           = "Maxlinear Ethernet GPY212B",
588                 .get_features   = genphy_c45_pma_read_abilities,
589                 .config_init    = gpy_config_init,
590                 .probe          = gpy_probe,
591                 .suspend        = genphy_suspend,
592                 .resume         = genphy_resume,
593                 .config_aneg    = gpy_config_aneg,
594                 .aneg_done      = genphy_c45_aneg_done,
595                 .read_status    = gpy_read_status,
596                 .config_intr    = gpy_config_intr,
597                 .handle_interrupt = gpy_handle_interrupt,
598                 .set_wol        = gpy_set_wol,
599                 .get_wol        = gpy_get_wol,
600                 .set_loopback   = gpy_loopback,
601         },
602         {
603                 PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
604                 .name           = "Maxlinear Ethernet GPY212C",
605                 .get_features   = genphy_c45_pma_read_abilities,
606                 .config_init    = gpy_config_init,
607                 .probe          = gpy_probe,
608                 .suspend        = genphy_suspend,
609                 .resume         = genphy_resume,
610                 .config_aneg    = gpy_config_aneg,
611                 .aneg_done      = genphy_c45_aneg_done,
612                 .read_status    = gpy_read_status,
613                 .config_intr    = gpy_config_intr,
614                 .handle_interrupt = gpy_handle_interrupt,
615                 .set_wol        = gpy_set_wol,
616                 .get_wol        = gpy_get_wol,
617                 .set_loopback   = gpy_loopback,
618         },
619         {
620                 .phy_id         = PHY_ID_GPY215B,
621                 .phy_id_mask    = PHY_ID_GPYx15B_MASK,
622                 .name           = "Maxlinear Ethernet GPY215B",
623                 .get_features   = genphy_c45_pma_read_abilities,
624                 .config_init    = gpy_config_init,
625                 .probe          = gpy_probe,
626                 .suspend        = genphy_suspend,
627                 .resume         = genphy_resume,
628                 .config_aneg    = gpy_config_aneg,
629                 .aneg_done      = genphy_c45_aneg_done,
630                 .read_status    = gpy_read_status,
631                 .config_intr    = gpy_config_intr,
632                 .handle_interrupt = gpy_handle_interrupt,
633                 .set_wol        = gpy_set_wol,
634                 .get_wol        = gpy_get_wol,
635                 .set_loopback   = gpy_loopback,
636         },
637         {
638                 PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
639                 .name           = "Maxlinear Ethernet GPY215C",
640                 .get_features   = genphy_c45_pma_read_abilities,
641                 .config_init    = gpy_config_init,
642                 .probe          = gpy_probe,
643                 .suspend        = genphy_suspend,
644                 .resume         = genphy_resume,
645                 .config_aneg    = gpy_config_aneg,
646                 .aneg_done      = genphy_c45_aneg_done,
647                 .read_status    = gpy_read_status,
648                 .config_intr    = gpy_config_intr,
649                 .handle_interrupt = gpy_handle_interrupt,
650                 .set_wol        = gpy_set_wol,
651                 .get_wol        = gpy_get_wol,
652                 .set_loopback   = gpy_loopback,
653         },
654         {
655                 PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
656                 .name           = "Maxlinear Ethernet GPY241B",
657                 .get_features   = genphy_c45_pma_read_abilities,
658                 .config_init    = gpy_config_init,
659                 .probe          = gpy_probe,
660                 .suspend        = genphy_suspend,
661                 .resume         = genphy_resume,
662                 .config_aneg    = gpy_config_aneg,
663                 .aneg_done      = genphy_c45_aneg_done,
664                 .read_status    = gpy_read_status,
665                 .config_intr    = gpy_config_intr,
666                 .handle_interrupt = gpy_handle_interrupt,
667                 .set_wol        = gpy_set_wol,
668                 .get_wol        = gpy_get_wol,
669                 .set_loopback   = gpy_loopback,
670         },
671         {
672                 PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
673                 .name           = "Maxlinear Ethernet GPY241BM",
674                 .get_features   = genphy_c45_pma_read_abilities,
675                 .config_init    = gpy_config_init,
676                 .probe          = gpy_probe,
677                 .suspend        = genphy_suspend,
678                 .resume         = genphy_resume,
679                 .config_aneg    = gpy_config_aneg,
680                 .aneg_done      = genphy_c45_aneg_done,
681                 .read_status    = gpy_read_status,
682                 .config_intr    = gpy_config_intr,
683                 .handle_interrupt = gpy_handle_interrupt,
684                 .set_wol        = gpy_set_wol,
685                 .get_wol        = gpy_get_wol,
686                 .set_loopback   = gpy_loopback,
687         },
688         {
689                 PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
690                 .name           = "Maxlinear Ethernet GPY245B",
691                 .get_features   = genphy_c45_pma_read_abilities,
692                 .config_init    = gpy_config_init,
693                 .probe          = gpy_probe,
694                 .suspend        = genphy_suspend,
695                 .resume         = genphy_resume,
696                 .config_aneg    = gpy_config_aneg,
697                 .aneg_done      = genphy_c45_aneg_done,
698                 .read_status    = gpy_read_status,
699                 .config_intr    = gpy_config_intr,
700                 .handle_interrupt = gpy_handle_interrupt,
701                 .set_wol        = gpy_set_wol,
702                 .get_wol        = gpy_get_wol,
703                 .set_loopback   = gpy_loopback,
704         },
705 };
706 module_phy_driver(gpy_drivers);
707
708 static struct mdio_device_id __maybe_unused gpy_tbl[] = {
709         {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
710         {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
711         {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
712         {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
713         {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
714         {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
715         {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
716         {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
717         {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
718         {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
719         {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
720         {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
721         { }
722 };
723 MODULE_DEVICE_TABLE(mdio, gpy_tbl);
724
725 MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
726 MODULE_AUTHOR("Xu Liang");
727 MODULE_LICENSE("GPL");