xref: /openbmc/linux/drivers/net/phy/broadcom.c (revision 6774def6)
1 /*
2  *	drivers/net/phy/broadcom.c
3  *
4  *	Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
5  *	transceivers.
6  *
7  *	Copyright (c) 2006  Maciej W. Rozycki
8  *
9  *	Inspired by code written by Amy Fong.
10  *
11  *	This program is free software; you can redistribute it and/or
12  *	modify it under the terms of the GNU General Public License
13  *	as published by the Free Software Foundation; either version
14  *	2 of the License, or (at your option) any later version.
15  */
16 
17 #include <linux/module.h>
18 #include <linux/phy.h>
19 #include <linux/brcmphy.h>
20 
21 
22 #define BRCM_PHY_MODEL(phydev) \
23 	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
24 
25 #define BRCM_PHY_REV(phydev) \
26 	((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
27 
28 MODULE_DESCRIPTION("Broadcom PHY driver");
29 MODULE_AUTHOR("Maciej W. Rozycki");
30 MODULE_LICENSE("GPL");
31 
32 /* Indirect register access functions for the Expansion Registers */
33 static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum)
34 {
35 	int val;
36 
37 	val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
38 	if (val < 0)
39 		return val;
40 
41 	val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
42 
43 	/* Restore default value.  It's O.K. if this write fails. */
44 	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
45 
46 	return val;
47 }
48 
49 static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val)
50 {
51 	int ret;
52 
53 	ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
54 	if (ret < 0)
55 		return ret;
56 
57 	ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
58 
59 	/* Restore default value.  It's O.K. if this write fails. */
60 	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
61 
62 	return ret;
63 }
64 
65 static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
66 {
67 	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
68 }
69 
70 /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
71 static int bcm50610_a0_workaround(struct phy_device *phydev)
72 {
73 	int err;
74 
75 	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0,
76 				MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
77 				MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
78 	if (err < 0)
79 		return err;
80 
81 	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3,
82 					MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
83 	if (err < 0)
84 		return err;
85 
86 	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75,
87 				MII_BCM54XX_EXP_EXP75_VDACCTRL);
88 	if (err < 0)
89 		return err;
90 
91 	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96,
92 				MII_BCM54XX_EXP_EXP96_MYST);
93 	if (err < 0)
94 		return err;
95 
96 	err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97,
97 				MII_BCM54XX_EXP_EXP97_MYST);
98 
99 	return err;
100 }
101 
102 static int bcm54xx_phydsp_config(struct phy_device *phydev)
103 {
104 	int err, err2;
105 
106 	/* Enable the SMDSP clock */
107 	err = bcm54xx_auxctl_write(phydev,
108 				   MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
109 				   MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
110 				   MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
111 	if (err < 0)
112 		return err;
113 
114 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
115 	    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
116 		/* Clear bit 9 to fix a phy interop issue. */
117 		err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
118 					MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
119 		if (err < 0)
120 			goto error;
121 
122 		if (phydev->drv->phy_id == PHY_ID_BCM50610) {
123 			err = bcm50610_a0_workaround(phydev);
124 			if (err < 0)
125 				goto error;
126 		}
127 	}
128 
129 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
130 		int val;
131 
132 		val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75);
133 		if (val < 0)
134 			goto error;
135 
136 		val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
137 		err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val);
138 	}
139 
140 error:
141 	/* Disable the SMDSP clock */
142 	err2 = bcm54xx_auxctl_write(phydev,
143 				    MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
144 				    MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
145 
146 	/* Return the first error reported. */
147 	return err ? err : err2;
148 }
149 
150 static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
151 {
152 	u32 orig;
153 	int val;
154 	bool clk125en = true;
155 
156 	/* Abort if we are using an untested phy. */
157 	if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
158 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
159 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
160 		return;
161 
162 	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
163 	if (val < 0)
164 		return;
165 
166 	orig = val;
167 
168 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
169 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
170 	    BRCM_PHY_REV(phydev) >= 0x3) {
171 		/*
172 		 * Here, bit 0 _disables_ CLK125 when set.
173 		 * This bit is set by default.
174 		 */
175 		clk125en = false;
176 	} else {
177 		if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
178 			/* Here, bit 0 _enables_ CLK125 when set */
179 			val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
180 			clk125en = false;
181 		}
182 	}
183 
184 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
185 		val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
186 	else
187 		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
188 
189 	if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
190 		val |= BCM54XX_SHD_SCR3_TRDDAPD;
191 
192 	if (orig != val)
193 		bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
194 
195 	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
196 	if (val < 0)
197 		return;
198 
199 	orig = val;
200 
201 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
202 		val |= BCM54XX_SHD_APD_EN;
203 	else
204 		val &= ~BCM54XX_SHD_APD_EN;
205 
206 	if (orig != val)
207 		bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
208 }
209 
210 static int bcm54xx_config_init(struct phy_device *phydev)
211 {
212 	int reg, err;
213 
214 	reg = phy_read(phydev, MII_BCM54XX_ECR);
215 	if (reg < 0)
216 		return reg;
217 
218 	/* Mask interrupts globally.  */
219 	reg |= MII_BCM54XX_ECR_IM;
220 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
221 	if (err < 0)
222 		return err;
223 
224 	/* Unmask events we are interested in.  */
225 	reg = ~(MII_BCM54XX_INT_DUPLEX |
226 		MII_BCM54XX_INT_SPEED |
227 		MII_BCM54XX_INT_LINK);
228 	err = phy_write(phydev, MII_BCM54XX_IMR, reg);
229 	if (err < 0)
230 		return err;
231 
232 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
233 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
234 	    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
235 		bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0);
236 
237 	if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
238 	    (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
239 	    (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
240 		bcm54xx_adjust_rxrefclk(phydev);
241 
242 	bcm54xx_phydsp_config(phydev);
243 
244 	return 0;
245 }
246 
247 static int bcm5482_config_init(struct phy_device *phydev)
248 {
249 	int err, reg;
250 
251 	err = bcm54xx_config_init(phydev);
252 
253 	if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
254 		/*
255 		 * Enable secondary SerDes and its use as an LED source
256 		 */
257 		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD);
258 		bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD,
259 				     reg |
260 				     BCM5482_SHD_SSD_LEDM |
261 				     BCM5482_SHD_SSD_EN);
262 
263 		/*
264 		 * Enable SGMII slave mode and auto-detection
265 		 */
266 		reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
267 		err = bcm54xx_exp_read(phydev, reg);
268 		if (err < 0)
269 			return err;
270 		err = bcm54xx_exp_write(phydev, reg, err |
271 					BCM5482_SSD_SGMII_SLAVE_EN |
272 					BCM5482_SSD_SGMII_SLAVE_AD);
273 		if (err < 0)
274 			return err;
275 
276 		/*
277 		 * Disable secondary SerDes powerdown
278 		 */
279 		reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
280 		err = bcm54xx_exp_read(phydev, reg);
281 		if (err < 0)
282 			return err;
283 		err = bcm54xx_exp_write(phydev, reg,
284 					err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
285 		if (err < 0)
286 			return err;
287 
288 		/*
289 		 * Select 1000BASE-X register set (primary SerDes)
290 		 */
291 		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE);
292 		bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE,
293 				     reg | BCM5482_SHD_MODE_1000BX);
294 
295 		/*
296 		 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
297 		 * (Use LED1 as secondary SerDes ACTIVITY LED)
298 		 */
299 		bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1,
300 			BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
301 			BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
302 
303 		/*
304 		 * Auto-negotiation doesn't seem to work quite right
305 		 * in this mode, so we disable it and force it to the
306 		 * right speed/duplex setting.  Only 'link status'
307 		 * is important.
308 		 */
309 		phydev->autoneg = AUTONEG_DISABLE;
310 		phydev->speed = SPEED_1000;
311 		phydev->duplex = DUPLEX_FULL;
312 	}
313 
314 	return err;
315 }
316 
317 static int bcm5482_read_status(struct phy_device *phydev)
318 {
319 	int err;
320 
321 	err = genphy_read_status(phydev);
322 
323 	if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
324 		/*
325 		 * Only link status matters for 1000Base-X mode, so force
326 		 * 1000 Mbit/s full-duplex status
327 		 */
328 		if (phydev->link) {
329 			phydev->speed = SPEED_1000;
330 			phydev->duplex = DUPLEX_FULL;
331 		}
332 	}
333 
334 	return err;
335 }
336 
337 static int bcm54xx_ack_interrupt(struct phy_device *phydev)
338 {
339 	int reg;
340 
341 	/* Clear pending interrupts.  */
342 	reg = phy_read(phydev, MII_BCM54XX_ISR);
343 	if (reg < 0)
344 		return reg;
345 
346 	return 0;
347 }
348 
349 static int bcm54xx_config_intr(struct phy_device *phydev)
350 {
351 	int reg, err;
352 
353 	reg = phy_read(phydev, MII_BCM54XX_ECR);
354 	if (reg < 0)
355 		return reg;
356 
357 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
358 		reg &= ~MII_BCM54XX_ECR_IM;
359 	else
360 		reg |= MII_BCM54XX_ECR_IM;
361 
362 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
363 	return err;
364 }
365 
366 static int bcm5481_config_aneg(struct phy_device *phydev)
367 {
368 	int ret;
369 
370 	/* Aneg firsly. */
371 	ret = genphy_config_aneg(phydev);
372 
373 	/* Then we can set up the delay. */
374 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
375 		u16 reg;
376 
377 		/*
378 		 * There is no BCM5481 specification available, so down
379 		 * here is everything we know about "register 0x18". This
380 		 * at least helps BCM5481 to successfully receive packets
381 		 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
382 		 * says: "This sets delay between the RXD and RXC signals
383 		 * instead of using trace lengths to achieve timing".
384 		 */
385 
386 		/* Set RDX clk delay. */
387 		reg = 0x7 | (0x7 << 12);
388 		phy_write(phydev, 0x18, reg);
389 
390 		reg = phy_read(phydev, 0x18);
391 		/* Set RDX-RXC skew. */
392 		reg |= (1 << 8);
393 		/* Write bits 14:0. */
394 		reg |= (1 << 15);
395 		phy_write(phydev, 0x18, reg);
396 	}
397 
398 	return ret;
399 }
400 
401 static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
402 {
403 	int val;
404 
405 	val = phy_read(phydev, reg);
406 	if (val < 0)
407 		return val;
408 
409 	return phy_write(phydev, reg, val | set);
410 }
411 
412 static int brcm_fet_config_init(struct phy_device *phydev)
413 {
414 	int reg, err, err2, brcmtest;
415 
416 	/* Reset the PHY to bring it to a known state. */
417 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
418 	if (err < 0)
419 		return err;
420 
421 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
422 	if (reg < 0)
423 		return reg;
424 
425 	/* Unmask events we are interested in and mask interrupts globally. */
426 	reg = MII_BRCM_FET_IR_DUPLEX_EN |
427 	      MII_BRCM_FET_IR_SPEED_EN |
428 	      MII_BRCM_FET_IR_LINK_EN |
429 	      MII_BRCM_FET_IR_ENABLE |
430 	      MII_BRCM_FET_IR_MASK;
431 
432 	err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
433 	if (err < 0)
434 		return err;
435 
436 	/* Enable shadow register access */
437 	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
438 	if (brcmtest < 0)
439 		return brcmtest;
440 
441 	reg = brcmtest | MII_BRCM_FET_BT_SRE;
442 
443 	err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
444 	if (err < 0)
445 		return err;
446 
447 	/* Set the LED mode */
448 	reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
449 	if (reg < 0) {
450 		err = reg;
451 		goto done;
452 	}
453 
454 	reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
455 	reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
456 
457 	err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
458 	if (err < 0)
459 		goto done;
460 
461 	/* Enable auto MDIX */
462 	err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
463 				       MII_BRCM_FET_SHDW_MC_FAME);
464 	if (err < 0)
465 		goto done;
466 
467 	if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
468 		/* Enable auto power down */
469 		err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
470 					       MII_BRCM_FET_SHDW_AS2_APDE);
471 	}
472 
473 done:
474 	/* Disable shadow register access */
475 	err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
476 	if (!err)
477 		err = err2;
478 
479 	return err;
480 }
481 
482 static int brcm_fet_ack_interrupt(struct phy_device *phydev)
483 {
484 	int reg;
485 
486 	/* Clear pending interrupts.  */
487 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
488 	if (reg < 0)
489 		return reg;
490 
491 	return 0;
492 }
493 
494 static int brcm_fet_config_intr(struct phy_device *phydev)
495 {
496 	int reg, err;
497 
498 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
499 	if (reg < 0)
500 		return reg;
501 
502 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
503 		reg &= ~MII_BRCM_FET_IR_MASK;
504 	else
505 		reg |= MII_BRCM_FET_IR_MASK;
506 
507 	err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
508 	return err;
509 }
510 
511 static struct phy_driver broadcom_drivers[] = {
512 {
513 	.phy_id		= PHY_ID_BCM5411,
514 	.phy_id_mask	= 0xfffffff0,
515 	.name		= "Broadcom BCM5411",
516 	.features	= PHY_GBIT_FEATURES |
517 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
518 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
519 	.config_init	= bcm54xx_config_init,
520 	.config_aneg	= genphy_config_aneg,
521 	.read_status	= genphy_read_status,
522 	.ack_interrupt	= bcm54xx_ack_interrupt,
523 	.config_intr	= bcm54xx_config_intr,
524 	.driver		= { .owner = THIS_MODULE },
525 }, {
526 	.phy_id		= PHY_ID_BCM5421,
527 	.phy_id_mask	= 0xfffffff0,
528 	.name		= "Broadcom BCM5421",
529 	.features	= PHY_GBIT_FEATURES |
530 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
531 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
532 	.config_init	= bcm54xx_config_init,
533 	.config_aneg	= genphy_config_aneg,
534 	.read_status	= genphy_read_status,
535 	.ack_interrupt	= bcm54xx_ack_interrupt,
536 	.config_intr	= bcm54xx_config_intr,
537 	.driver		= { .owner = THIS_MODULE },
538 }, {
539 	.phy_id		= PHY_ID_BCM5461,
540 	.phy_id_mask	= 0xfffffff0,
541 	.name		= "Broadcom BCM5461",
542 	.features	= PHY_GBIT_FEATURES |
543 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
544 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
545 	.config_init	= bcm54xx_config_init,
546 	.config_aneg	= genphy_config_aneg,
547 	.read_status	= genphy_read_status,
548 	.ack_interrupt	= bcm54xx_ack_interrupt,
549 	.config_intr	= bcm54xx_config_intr,
550 	.driver		= { .owner = THIS_MODULE },
551 }, {
552 	.phy_id		= PHY_ID_BCM5464,
553 	.phy_id_mask	= 0xfffffff0,
554 	.name		= "Broadcom BCM5464",
555 	.features	= PHY_GBIT_FEATURES |
556 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
557 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
558 	.config_init	= bcm54xx_config_init,
559 	.config_aneg	= genphy_config_aneg,
560 	.read_status	= genphy_read_status,
561 	.ack_interrupt	= bcm54xx_ack_interrupt,
562 	.config_intr	= bcm54xx_config_intr,
563 	.driver		= { .owner = THIS_MODULE },
564 }, {
565 	.phy_id		= PHY_ID_BCM5481,
566 	.phy_id_mask	= 0xfffffff0,
567 	.name		= "Broadcom BCM5481",
568 	.features	= PHY_GBIT_FEATURES |
569 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
570 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
571 	.config_init	= bcm54xx_config_init,
572 	.config_aneg	= bcm5481_config_aneg,
573 	.read_status	= genphy_read_status,
574 	.ack_interrupt	= bcm54xx_ack_interrupt,
575 	.config_intr	= bcm54xx_config_intr,
576 	.driver		= { .owner = THIS_MODULE },
577 }, {
578 	.phy_id		= PHY_ID_BCM5482,
579 	.phy_id_mask	= 0xfffffff0,
580 	.name		= "Broadcom BCM5482",
581 	.features	= PHY_GBIT_FEATURES |
582 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
583 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
584 	.config_init	= bcm5482_config_init,
585 	.config_aneg	= genphy_config_aneg,
586 	.read_status	= bcm5482_read_status,
587 	.ack_interrupt	= bcm54xx_ack_interrupt,
588 	.config_intr	= bcm54xx_config_intr,
589 	.driver		= { .owner = THIS_MODULE },
590 }, {
591 	.phy_id		= PHY_ID_BCM50610,
592 	.phy_id_mask	= 0xfffffff0,
593 	.name		= "Broadcom BCM50610",
594 	.features	= PHY_GBIT_FEATURES |
595 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
596 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
597 	.config_init	= bcm54xx_config_init,
598 	.config_aneg	= genphy_config_aneg,
599 	.read_status	= genphy_read_status,
600 	.ack_interrupt	= bcm54xx_ack_interrupt,
601 	.config_intr	= bcm54xx_config_intr,
602 	.driver		= { .owner = THIS_MODULE },
603 }, {
604 	.phy_id		= PHY_ID_BCM50610M,
605 	.phy_id_mask	= 0xfffffff0,
606 	.name		= "Broadcom BCM50610M",
607 	.features	= PHY_GBIT_FEATURES |
608 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
609 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
610 	.config_init	= bcm54xx_config_init,
611 	.config_aneg	= genphy_config_aneg,
612 	.read_status	= genphy_read_status,
613 	.ack_interrupt	= bcm54xx_ack_interrupt,
614 	.config_intr	= bcm54xx_config_intr,
615 	.driver		= { .owner = THIS_MODULE },
616 }, {
617 	.phy_id		= PHY_ID_BCM57780,
618 	.phy_id_mask	= 0xfffffff0,
619 	.name		= "Broadcom BCM57780",
620 	.features	= PHY_GBIT_FEATURES |
621 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
622 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
623 	.config_init	= bcm54xx_config_init,
624 	.config_aneg	= genphy_config_aneg,
625 	.read_status	= genphy_read_status,
626 	.ack_interrupt	= bcm54xx_ack_interrupt,
627 	.config_intr	= bcm54xx_config_intr,
628 	.driver		= { .owner = THIS_MODULE },
629 }, {
630 	.phy_id		= PHY_ID_BCMAC131,
631 	.phy_id_mask	= 0xfffffff0,
632 	.name		= "Broadcom BCMAC131",
633 	.features	= PHY_BASIC_FEATURES |
634 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
635 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
636 	.config_init	= brcm_fet_config_init,
637 	.config_aneg	= genphy_config_aneg,
638 	.read_status	= genphy_read_status,
639 	.ack_interrupt	= brcm_fet_ack_interrupt,
640 	.config_intr	= brcm_fet_config_intr,
641 	.driver		= { .owner = THIS_MODULE },
642 }, {
643 	.phy_id		= PHY_ID_BCM5241,
644 	.phy_id_mask	= 0xfffffff0,
645 	.name		= "Broadcom BCM5241",
646 	.features	= PHY_BASIC_FEATURES |
647 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
648 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
649 	.config_init	= brcm_fet_config_init,
650 	.config_aneg	= genphy_config_aneg,
651 	.read_status	= genphy_read_status,
652 	.ack_interrupt	= brcm_fet_ack_interrupt,
653 	.config_intr	= brcm_fet_config_intr,
654 	.driver		= { .owner = THIS_MODULE },
655 } };
656 
657 static int __init broadcom_init(void)
658 {
659 	return phy_drivers_register(broadcom_drivers,
660 		ARRAY_SIZE(broadcom_drivers));
661 }
662 
663 static void __exit broadcom_exit(void)
664 {
665 	phy_drivers_unregister(broadcom_drivers,
666 		ARRAY_SIZE(broadcom_drivers));
667 }
668 
669 module_init(broadcom_init);
670 module_exit(broadcom_exit);
671 
672 static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
673 	{ PHY_ID_BCM5411, 0xfffffff0 },
674 	{ PHY_ID_BCM5421, 0xfffffff0 },
675 	{ PHY_ID_BCM5461, 0xfffffff0 },
676 	{ PHY_ID_BCM5464, 0xfffffff0 },
677 	{ PHY_ID_BCM5482, 0xfffffff0 },
678 	{ PHY_ID_BCM5482, 0xfffffff0 },
679 	{ PHY_ID_BCM50610, 0xfffffff0 },
680 	{ PHY_ID_BCM50610M, 0xfffffff0 },
681 	{ PHY_ID_BCM57780, 0xfffffff0 },
682 	{ PHY_ID_BCMAC131, 0xfffffff0 },
683 	{ PHY_ID_BCM5241, 0xfffffff0 },
684 	{ }
685 };
686 
687 MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
688