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
28MODULE_DESCRIPTION("Broadcom PHY driver");
29MODULE_AUTHOR("Maciej W. Rozycki");
30MODULE_LICENSE("GPL");
31
32/* Indirect register access functions for the Expansion Registers */
33static 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
49static 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
65static 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() */
71static 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
102static 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
140error:
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
150static 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
210static 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
247static 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
317static 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
337static 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
349static 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
366static 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
401static 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
412static 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
473done:
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
482static 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
494static 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
511static 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_BCM54616S,
553	.phy_id_mask	= 0xfffffff0,
554	.name		= "Broadcom BCM54616S",
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_BCM5464,
566	.phy_id_mask	= 0xfffffff0,
567	.name		= "Broadcom BCM5464",
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	= genphy_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_BCM5481,
579	.phy_id_mask	= 0xfffffff0,
580	.name		= "Broadcom BCM5481",
581	.features	= PHY_GBIT_FEATURES |
582			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
583	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
584	.config_init	= bcm54xx_config_init,
585	.config_aneg	= bcm5481_config_aneg,
586	.read_status	= genphy_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_BCM5482,
592	.phy_id_mask	= 0xfffffff0,
593	.name		= "Broadcom BCM5482",
594	.features	= PHY_GBIT_FEATURES |
595			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
596	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
597	.config_init	= bcm5482_config_init,
598	.config_aneg	= genphy_config_aneg,
599	.read_status	= bcm5482_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_BCM50610,
605	.phy_id_mask	= 0xfffffff0,
606	.name		= "Broadcom BCM50610",
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_BCM50610M,
618	.phy_id_mask	= 0xfffffff0,
619	.name		= "Broadcom BCM50610M",
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_BCM57780,
631	.phy_id_mask	= 0xfffffff0,
632	.name		= "Broadcom BCM57780",
633	.features	= PHY_GBIT_FEATURES |
634			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
635	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
636	.config_init	= bcm54xx_config_init,
637	.config_aneg	= genphy_config_aneg,
638	.read_status	= genphy_read_status,
639	.ack_interrupt	= bcm54xx_ack_interrupt,
640	.config_intr	= bcm54xx_config_intr,
641	.driver		= { .owner = THIS_MODULE },
642}, {
643	.phy_id		= PHY_ID_BCMAC131,
644	.phy_id_mask	= 0xfffffff0,
645	.name		= "Broadcom BCMAC131",
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	.phy_id		= PHY_ID_BCM5241,
657	.phy_id_mask	= 0xfffffff0,
658	.name		= "Broadcom BCM5241",
659	.features	= PHY_BASIC_FEATURES |
660			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
661	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
662	.config_init	= brcm_fet_config_init,
663	.config_aneg	= genphy_config_aneg,
664	.read_status	= genphy_read_status,
665	.ack_interrupt	= brcm_fet_ack_interrupt,
666	.config_intr	= brcm_fet_config_intr,
667	.driver		= { .owner = THIS_MODULE },
668} };
669
670module_phy_driver(broadcom_drivers);
671
672static 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_BCM54616S, 0xfffffff0 },
677	{ PHY_ID_BCM5464, 0xfffffff0 },
678	{ PHY_ID_BCM5481, 0xfffffff0 },
679	{ PHY_ID_BCM5482, 0xfffffff0 },
680	{ PHY_ID_BCM50610, 0xfffffff0 },
681	{ PHY_ID_BCM50610M, 0xfffffff0 },
682	{ PHY_ID_BCM57780, 0xfffffff0 },
683	{ PHY_ID_BCMAC131, 0xfffffff0 },
684	{ PHY_ID_BCM5241, 0xfffffff0 },
685	{ }
686};
687
688MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
689