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