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