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