blob: 7b10495fe8fb9a6f9dcdb82428ce806944820d40 [file] [log] [blame]
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +01001/*
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>
Matt Carlson8649f132009-11-02 14:30:00 +000019#include <linux/brcmphy.h>
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +010020
Matt Carlson772638b2008-11-03 16:56:51 -080021#define PHY_ID_BCM50610 0x0143bd60
Matt Carlson4f4598f2009-08-25 10:10:30 +000022#define PHY_ID_BCM50610M 0x0143bd70
Matt Carlsond9221e62009-08-25 10:11:26 +000023#define PHY_ID_BCM57780 0x03625d90
24
25#define BRCM_PHY_MODEL(phydev) \
26 ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
27
Matt Carlson32e5a8d2009-11-02 14:31:39 +000028#define BRCM_PHY_REV(phydev) \
29 ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
30
Matt Carlson772638b2008-11-03 16:56:51 -080031
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +010032#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
33#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
34#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
35
36#define MII_BCM54XX_ESR 0x11 /* BCM54xx extended status register */
37#define MII_BCM54XX_ESR_IS 0x1000 /* Interrupt status */
38
Nate Casecd9af3d2008-05-17 06:40:39 +010039#define MII_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
40#define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
41#define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
42#define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
43
44#define MII_BCM54XX_AUX_CTL 0x18 /* Auxiliary control register */
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +010045#define MII_BCM54XX_ISR 0x1a /* BCM54xx interrupt status register */
46#define MII_BCM54XX_IMR 0x1b /* BCM54xx interrupt mask register */
47#define MII_BCM54XX_INT_CRCERR 0x0001 /* CRC error */
48#define MII_BCM54XX_INT_LINK 0x0002 /* Link status changed */
49#define MII_BCM54XX_INT_SPEED 0x0004 /* Link speed change */
50#define MII_BCM54XX_INT_DUPLEX 0x0008 /* Duplex mode changed */
51#define MII_BCM54XX_INT_LRS 0x0010 /* Local receiver status changed */
52#define MII_BCM54XX_INT_RRS 0x0020 /* Remote receiver status changed */
53#define MII_BCM54XX_INT_SSERR 0x0040 /* Scrambler synchronization error */
54#define MII_BCM54XX_INT_UHCD 0x0080 /* Unsupported HCD negotiated */
55#define MII_BCM54XX_INT_NHCD 0x0100 /* No HCD */
56#define MII_BCM54XX_INT_NHCDL 0x0200 /* No HCD link */
57#define MII_BCM54XX_INT_ANPR 0x0400 /* Auto-negotiation page received */
58#define MII_BCM54XX_INT_LC 0x0800 /* All counters below 128 */
59#define MII_BCM54XX_INT_HC 0x1000 /* Counter above 32768 */
60#define MII_BCM54XX_INT_MDIX 0x2000 /* MDIX status change */
61#define MII_BCM54XX_INT_PSERR 0x4000 /* Pair swap error */
62
Nate Casecd9af3d2008-05-17 06:40:39 +010063#define MII_BCM54XX_SHD 0x1c /* 0x1c shadow registers */
64#define MII_BCM54XX_SHD_WRITE 0x8000
65#define MII_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
66#define MII_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
67
68/*
Matt Carlson772638b2008-11-03 16:56:51 -080069 * AUXILIARY CONTROL SHADOW ACCESS REGISTERS. (PHY REG 0x18)
70 */
71#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
72#define MII_BCM54XX_AUXCTL_ACTL_TX_6DB 0x0400
73#define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800
74
75#define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
76#define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
77#define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
78#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
79
80#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
81
82
83/*
Nate Casecd9af3d2008-05-17 06:40:39 +010084 * Broadcom LED source encodings. These are used in BCM5461, BCM5481,
85 * BCM5482, and possibly some others.
86 */
87#define BCM_LED_SRC_LINKSPD1 0x0
88#define BCM_LED_SRC_LINKSPD2 0x1
89#define BCM_LED_SRC_XMITLED 0x2
90#define BCM_LED_SRC_ACTIVITYLED 0x3
91#define BCM_LED_SRC_FDXLED 0x4
92#define BCM_LED_SRC_SLAVE 0x5
93#define BCM_LED_SRC_INTR 0x6
94#define BCM_LED_SRC_QUALITY 0x7
95#define BCM_LED_SRC_RCVLED 0x8
96#define BCM_LED_SRC_MULTICOLOR1 0xa
97#define BCM_LED_SRC_OPENSHORT 0xb
98#define BCM_LED_SRC_OFF 0xe /* Tied high */
99#define BCM_LED_SRC_ON 0xf /* Tied low */
100
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000101
Nate Casecd9af3d2008-05-17 06:40:39 +0100102/*
103 * BCM5482: Shadow registers
104 * Shadow values go into bits [14:10] of register 0x1c to select a shadow
105 * register to access.
106 */
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000107/* 00101: Spare Control Register 3 */
108#define BCM54XX_SHD_SCR3 0x05
109#define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001
Matt Carlsonc704dc22009-11-02 14:32:12 +0000110#define BCM54XX_SHD_SCR3_DLLAPD_DIS 0x0002
111
112/* 01010: Auto Power-Down */
113#define BCM54XX_SHD_APD 0x0a
114#define BCM54XX_SHD_APD_EN 0x0020
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000115
Nate Casecd9af3d2008-05-17 06:40:39 +0100116#define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */
117 /* LED3 / ~LINKSPD[2] selector */
118#define BCM5482_SHD_LEDS1_LED3(src) ((src & 0xf) << 4)
119 /* LED1 / ~LINKSPD[1] selector */
120#define BCM5482_SHD_LEDS1_LED1(src) ((src & 0xf) << 0)
Matt Carlson63a14ce2009-11-02 14:30:40 +0000121#define BCM54XX_SHD_RGMII_MODE 0x0b /* 01011: RGMII Mode Selector */
Nate Casecd9af3d2008-05-17 06:40:39 +0100122#define BCM5482_SHD_SSD 0x14 /* 10100: Secondary SerDes control */
123#define BCM5482_SHD_SSD_LEDM 0x0008 /* SSD LED Mode enable */
124#define BCM5482_SHD_SSD_EN 0x0001 /* SSD enable */
125#define BCM5482_SHD_MODE 0x1f /* 11111: Mode Control Register */
126#define BCM5482_SHD_MODE_1000BX 0x0001 /* Enable 1000BASE-X registers */
127
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000128
Nate Casecd9af3d2008-05-17 06:40:39 +0100129/*
Matt Carlson772638b2008-11-03 16:56:51 -0800130 * EXPANSION SHADOW ACCESS REGISTERS. (PHY REG 0x15, 0x16, and 0x17)
131 */
132#define MII_BCM54XX_EXP_AADJ1CH0 0x001f
133#define MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN 0x0200
134#define MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF 0x0100
135#define MII_BCM54XX_EXP_AADJ1CH3 0x601f
136#define MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ 0x0002
137#define MII_BCM54XX_EXP_EXP08 0x0F08
138#define MII_BCM54XX_EXP_EXP08_RJCT_2MHZ 0x0001
139#define MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE 0x0200
140#define MII_BCM54XX_EXP_EXP75 0x0f75
141#define MII_BCM54XX_EXP_EXP75_VDACCTRL 0x003c
Matt Carlsond9221e62009-08-25 10:11:26 +0000142#define MII_BCM54XX_EXP_EXP75_CM_OSC 0x0001
Matt Carlson772638b2008-11-03 16:56:51 -0800143#define MII_BCM54XX_EXP_EXP96 0x0f96
144#define MII_BCM54XX_EXP_EXP96_MYST 0x0010
145#define MII_BCM54XX_EXP_EXP97 0x0f97
146#define MII_BCM54XX_EXP_EXP97_MYST 0x0c0c
147
148/*
Nate Casecd9af3d2008-05-17 06:40:39 +0100149 * BCM5482: Secondary SerDes registers
150 */
151#define BCM5482_SSD_1000BX_CTL 0x00 /* 1000BASE-X Control */
152#define BCM5482_SSD_1000BX_CTL_PWRDOWN 0x0800 /* Power-down SSD */
153#define BCM5482_SSD_SGMII_SLAVE 0x15 /* SGMII Slave Register */
154#define BCM5482_SSD_SGMII_SLAVE_EN 0x0002 /* Slave mode enable */
155#define BCM5482_SSD_SGMII_SLAVE_AD 0x0001 /* Slave auto-detection */
156
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000157
158/*****************************************************************************/
159/* Fast Ethernet Transceiver definitions. */
160/*****************************************************************************/
161
162#define MII_BRCM_FET_INTREG 0x1a /* Interrupt register */
163#define MII_BRCM_FET_IR_MASK 0x0100 /* Mask all interrupts */
164#define MII_BRCM_FET_IR_LINK_EN 0x0200 /* Link status change enable */
165#define MII_BRCM_FET_IR_SPEED_EN 0x0400 /* Link speed change enable */
166#define MII_BRCM_FET_IR_DUPLEX_EN 0x0800 /* Duplex mode change enable */
167#define MII_BRCM_FET_IR_ENABLE 0x4000 /* Interrupt enable */
168
169#define MII_BRCM_FET_BRCMTEST 0x1f /* Brcm test register */
170#define MII_BRCM_FET_BT_SRE 0x0080 /* Shadow register enable */
171
172
173/*** Shadow register definitions ***/
174
175#define MII_BRCM_FET_SHDW_MISCCTRL 0x10 /* Shadow misc ctrl */
176#define MII_BRCM_FET_SHDW_MC_FAME 0x4000 /* Force Auto MDIX enable */
177
178#define MII_BRCM_FET_SHDW_AUXMODE4 0x1a /* Auxiliary mode 4 */
179#define MII_BRCM_FET_SHDW_AM4_LED_MASK 0x0003
180#define MII_BRCM_FET_SHDW_AM4_LED_MODE1 0x0001
181
182#define MII_BRCM_FET_SHDW_AUXSTAT2 0x1b /* Auxiliary status 2 */
183#define MII_BRCM_FET_SHDW_AS2_APDE 0x0020 /* Auto power down enable */
184
185
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100186MODULE_DESCRIPTION("Broadcom PHY driver");
187MODULE_AUTHOR("Maciej W. Rozycki");
188MODULE_LICENSE("GPL");
189
Nate Casecd9af3d2008-05-17 06:40:39 +0100190/*
191 * Indirect register access functions for the 1000BASE-T/100BASE-TX/10BASE-T
192 * 0x1c shadow registers.
193 */
194static int bcm54xx_shadow_read(struct phy_device *phydev, u16 shadow)
195{
196 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
197 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
198}
199
200static int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow, u16 val)
201{
202 return phy_write(phydev, MII_BCM54XX_SHD,
203 MII_BCM54XX_SHD_WRITE |
204 MII_BCM54XX_SHD_VAL(shadow) |
205 MII_BCM54XX_SHD_DATA(val));
206}
207
Matt Carlson042a75b2008-11-03 16:56:29 -0800208/* Indirect register access functions for the Expansion Registers */
Matt Carlsond9221e62009-08-25 10:11:26 +0000209static int bcm54xx_exp_read(struct phy_device *phydev, u16 regnum)
Nate Casecd9af3d2008-05-17 06:40:39 +0100210{
211 int val;
212
Matt Carlson042a75b2008-11-03 16:56:29 -0800213 val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
214 if (val < 0)
215 return val;
216
Nate Casecd9af3d2008-05-17 06:40:39 +0100217 val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
Matt Carlson042a75b2008-11-03 16:56:29 -0800218
219 /* Restore default value. It's O.K. if this write fails. */
220 phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
Nate Casecd9af3d2008-05-17 06:40:39 +0100221
222 return val;
223}
224
Matt Carlson772638b2008-11-03 16:56:51 -0800225static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val)
Nate Casecd9af3d2008-05-17 06:40:39 +0100226{
227 int ret;
228
Matt Carlson042a75b2008-11-03 16:56:29 -0800229 ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
230 if (ret < 0)
231 return ret;
232
Nate Casecd9af3d2008-05-17 06:40:39 +0100233 ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
Matt Carlson042a75b2008-11-03 16:56:29 -0800234
235 /* Restore default value. It's O.K. if this write fails. */
236 phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
Nate Casecd9af3d2008-05-17 06:40:39 +0100237
238 return ret;
239}
240
Matt Carlson772638b2008-11-03 16:56:51 -0800241static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
242{
243 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
244}
245
Matt Carlson47b1b532009-11-02 14:28:04 +0000246/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
Matt Carlson772638b2008-11-03 16:56:51 -0800247static int bcm50610_a0_workaround(struct phy_device *phydev)
248{
249 int err;
250
Matt Carlson47b1b532009-11-02 14:28:04 +0000251 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0,
252 MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
253 MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
254 if (err < 0)
255 return err;
256
257 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3,
258 MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
259 if (err < 0)
260 return err;
261
262 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75,
263 MII_BCM54XX_EXP_EXP75_VDACCTRL);
264 if (err < 0)
265 return err;
266
267 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96,
268 MII_BCM54XX_EXP_EXP96_MYST);
269 if (err < 0)
270 return err;
271
272 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97,
273 MII_BCM54XX_EXP_EXP97_MYST);
274
275 return err;
276}
277
278static int bcm54xx_phydsp_config(struct phy_device *phydev)
279{
280 int err, err2;
281
282 /* Enable the SMDSP clock */
Matt Carlson772638b2008-11-03 16:56:51 -0800283 err = bcm54xx_auxctl_write(phydev,
284 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
285 MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
286 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
287 if (err < 0)
288 return err;
289
Matt Carlson219c6ef2009-11-02 14:28:33 +0000290 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
291 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
292 /* Clear bit 9 to fix a phy interop issue. */
293 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
294 MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
295 if (err < 0)
296 goto error;
297
298 if (phydev->drv->phy_id == PHY_ID_BCM50610) {
299 err = bcm50610_a0_workaround(phydev);
300 if (err < 0)
301 goto error;
302 }
303 }
Matt Carlson772638b2008-11-03 16:56:51 -0800304
Matt Carlson47b1b532009-11-02 14:28:04 +0000305 if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
306 int val;
Matt Carlson772638b2008-11-03 16:56:51 -0800307
Matt Carlson47b1b532009-11-02 14:28:04 +0000308 val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75);
309 if (val < 0)
310 goto error;
Matt Carlson772638b2008-11-03 16:56:51 -0800311
Matt Carlson47b1b532009-11-02 14:28:04 +0000312 val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
313 err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val);
314 }
Matt Carlson772638b2008-11-03 16:56:51 -0800315
316error:
Matt Carlson47b1b532009-11-02 14:28:04 +0000317 /* Disable the SMDSP clock */
318 err2 = bcm54xx_auxctl_write(phydev,
319 MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
320 MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
Matt Carlson772638b2008-11-03 16:56:51 -0800321
Matt Carlson47b1b532009-11-02 14:28:04 +0000322 /* Return the first error reported. */
323 return err ? err : err2;
Matt Carlson772638b2008-11-03 16:56:51 -0800324}
325
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000326static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
327{
328 u32 val, orig;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000329 bool clk125en = true;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000330
331 /* Abort if we are using an untested phy. */
Matt Carlsonc704dc22009-11-02 14:32:12 +0000332 if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 ||
333 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 ||
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000334 BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
335 return;
336
337 val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
338 if (val < 0)
339 return;
340
341 orig = val;
342
Matt Carlsonc704dc22009-11-02 14:32:12 +0000343 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
344 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
345 BRCM_PHY_REV(phydev) >= 0x3) {
346 /*
347 * Here, bit 0 _disables_ CLK125 when set.
348 * This bit is set by default.
349 */
350 clk125en = false;
351 } else {
352 if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000353 /* Here, bit 0 _enables_ CLK125 when set */
354 val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
Matt Carlsonc704dc22009-11-02 14:32:12 +0000355 clk125en = false;
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000356 }
357 }
358
Matt Carlsonc704dc22009-11-02 14:32:12 +0000359 if (clk125en == false ||
360 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
361 val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
362 else
363 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
364
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000365 if (orig != val)
366 bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
Matt Carlsonc704dc22009-11-02 14:32:12 +0000367
368 val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
369 if (val < 0)
370 return;
371
372 orig = val;
373
374 if (clk125en == false ||
375 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
376 val |= BCM54XX_SHD_APD_EN;
377 else
378 val &= ~BCM54XX_SHD_APD_EN;
379
380 if (orig != val)
381 bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000382}
383
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100384static int bcm54xx_config_init(struct phy_device *phydev)
385{
386 int reg, err;
387
388 reg = phy_read(phydev, MII_BCM54XX_ECR);
389 if (reg < 0)
390 return reg;
391
392 /* Mask interrupts globally. */
393 reg |= MII_BCM54XX_ECR_IM;
394 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
395 if (err < 0)
396 return err;
397
398 /* Unmask events we are interested in. */
399 reg = ~(MII_BCM54XX_INT_DUPLEX |
400 MII_BCM54XX_INT_SPEED |
401 MII_BCM54XX_INT_LINK);
402 err = phy_write(phydev, MII_BCM54XX_IMR, reg);
403 if (err < 0)
404 return err;
Matt Carlson772638b2008-11-03 16:56:51 -0800405
Matt Carlson63a14ce2009-11-02 14:30:40 +0000406 if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
407 BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
408 (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
409 bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0);
410
Matt Carlsonc704dc22009-11-02 14:32:12 +0000411 if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
412 (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
Matt Carlson32e5a8d2009-11-02 14:31:39 +0000413 bcm54xx_adjust_rxrefclk(phydev);
414
Matt Carlson47b1b532009-11-02 14:28:04 +0000415 bcm54xx_phydsp_config(phydev);
Matt Carlsond9221e62009-08-25 10:11:26 +0000416
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100417 return 0;
418}
419
Nate Casecd9af3d2008-05-17 06:40:39 +0100420static int bcm5482_config_init(struct phy_device *phydev)
421{
422 int err, reg;
423
424 err = bcm54xx_config_init(phydev);
425
426 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
427 /*
428 * Enable secondary SerDes and its use as an LED source
429 */
430 reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD);
431 bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD,
432 reg |
433 BCM5482_SHD_SSD_LEDM |
434 BCM5482_SHD_SSD_EN);
435
436 /*
437 * Enable SGMII slave mode and auto-detection
438 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800439 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
440 err = bcm54xx_exp_read(phydev, reg);
441 if (err < 0)
442 return err;
443 err = bcm54xx_exp_write(phydev, reg, err |
444 BCM5482_SSD_SGMII_SLAVE_EN |
445 BCM5482_SSD_SGMII_SLAVE_AD);
446 if (err < 0)
447 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100448
449 /*
450 * Disable secondary SerDes powerdown
451 */
Matt Carlson042a75b2008-11-03 16:56:29 -0800452 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
453 err = bcm54xx_exp_read(phydev, reg);
454 if (err < 0)
455 return err;
456 err = bcm54xx_exp_write(phydev, reg,
457 err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
458 if (err < 0)
459 return err;
Nate Casecd9af3d2008-05-17 06:40:39 +0100460
461 /*
462 * Select 1000BASE-X register set (primary SerDes)
463 */
464 reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE);
465 bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE,
466 reg | BCM5482_SHD_MODE_1000BX);
467
468 /*
469 * LED1=ACTIVITYLED, LED3=LINKSPD[2]
470 * (Use LED1 as secondary SerDes ACTIVITY LED)
471 */
472 bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1,
473 BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
474 BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
475
476 /*
477 * Auto-negotiation doesn't seem to work quite right
478 * in this mode, so we disable it and force it to the
479 * right speed/duplex setting. Only 'link status'
480 * is important.
481 */
482 phydev->autoneg = AUTONEG_DISABLE;
483 phydev->speed = SPEED_1000;
484 phydev->duplex = DUPLEX_FULL;
485 }
486
487 return err;
488}
489
490static int bcm5482_read_status(struct phy_device *phydev)
491{
492 int err;
493
494 err = genphy_read_status(phydev);
495
496 if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
497 /*
498 * Only link status matters for 1000Base-X mode, so force
499 * 1000 Mbit/s full-duplex status
500 */
501 if (phydev->link) {
502 phydev->speed = SPEED_1000;
503 phydev->duplex = DUPLEX_FULL;
504 }
505 }
506
507 return err;
508}
509
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100510static int bcm54xx_ack_interrupt(struct phy_device *phydev)
511{
512 int reg;
513
514 /* Clear pending interrupts. */
515 reg = phy_read(phydev, MII_BCM54XX_ISR);
516 if (reg < 0)
517 return reg;
518
519 return 0;
520}
521
522static int bcm54xx_config_intr(struct phy_device *phydev)
523{
524 int reg, err;
525
526 reg = phy_read(phydev, MII_BCM54XX_ECR);
527 if (reg < 0)
528 return reg;
529
530 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
531 reg &= ~MII_BCM54XX_ECR_IM;
532 else
533 reg |= MII_BCM54XX_ECR_IM;
534
535 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
536 return err;
537}
538
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300539static int bcm5481_config_aneg(struct phy_device *phydev)
540{
541 int ret;
542
543 /* Aneg firsly. */
544 ret = genphy_config_aneg(phydev);
545
546 /* Then we can set up the delay. */
547 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
548 u16 reg;
549
550 /*
551 * There is no BCM5481 specification available, so down
552 * here is everything we know about "register 0x18". This
553 * at least helps BCM5481 to successfuly receive packets
554 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
555 * says: "This sets delay between the RXD and RXC signals
556 * instead of using trace lengths to achieve timing".
557 */
558
559 /* Set RDX clk delay. */
560 reg = 0x7 | (0x7 << 12);
561 phy_write(phydev, 0x18, reg);
562
563 reg = phy_read(phydev, 0x18);
564 /* Set RDX-RXC skew. */
565 reg |= (1 << 8);
566 /* Write bits 14:0. */
567 reg |= (1 << 15);
568 phy_write(phydev, 0x18, reg);
569 }
570
571 return ret;
572}
573
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000574static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
575{
576 int val;
577
578 val = phy_read(phydev, reg);
579 if (val < 0)
580 return val;
581
582 return phy_write(phydev, reg, val | set);
583}
584
585static int brcm_fet_config_init(struct phy_device *phydev)
586{
587 int reg, err, err2, brcmtest;
588
589 /* Reset the PHY to bring it to a known state. */
590 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
591 if (err < 0)
592 return err;
593
594 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
595 if (reg < 0)
596 return reg;
597
598 /* Unmask events we are interested in and mask interrupts globally. */
599 reg = MII_BRCM_FET_IR_DUPLEX_EN |
600 MII_BRCM_FET_IR_SPEED_EN |
601 MII_BRCM_FET_IR_LINK_EN |
602 MII_BRCM_FET_IR_ENABLE |
603 MII_BRCM_FET_IR_MASK;
604
605 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
606 if (err < 0)
607 return err;
608
609 /* Enable shadow register access */
610 brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
611 if (brcmtest < 0)
612 return brcmtest;
613
614 reg = brcmtest | MII_BRCM_FET_BT_SRE;
615
616 err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
617 if (err < 0)
618 return err;
619
620 /* Set the LED mode */
621 reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
622 if (reg < 0) {
623 err = reg;
624 goto done;
625 }
626
627 reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
628 reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
629
630 err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
631 if (err < 0)
632 goto done;
633
634 /* Enable auto MDIX */
635 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
636 MII_BRCM_FET_SHDW_MC_FAME);
637 if (err < 0)
638 goto done;
639
Matt Carlsoncdd4e09d2009-11-02 14:31:11 +0000640 if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
641 /* Enable auto power down */
642 err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
643 MII_BRCM_FET_SHDW_AS2_APDE);
644 }
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000645
646done:
647 /* Disable shadow register access */
648 err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
649 if (!err)
650 err = err2;
651
652 return err;
653}
654
655static int brcm_fet_ack_interrupt(struct phy_device *phydev)
656{
657 int reg;
658
659 /* Clear pending interrupts. */
660 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
661 if (reg < 0)
662 return reg;
663
664 return 0;
665}
666
667static int brcm_fet_config_intr(struct phy_device *phydev)
668{
669 int reg, err;
670
671 reg = phy_read(phydev, MII_BRCM_FET_INTREG);
672 if (reg < 0)
673 return reg;
674
675 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
676 reg &= ~MII_BRCM_FET_IR_MASK;
677 else
678 reg |= MII_BRCM_FET_IR_MASK;
679
680 err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
681 return err;
682}
683
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100684static struct phy_driver bcm5411_driver = {
685 .phy_id = 0x00206070,
686 .phy_id_mask = 0xfffffff0,
687 .name = "Broadcom BCM5411",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800688 .features = PHY_GBIT_FEATURES |
689 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100690 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
691 .config_init = bcm54xx_config_init,
692 .config_aneg = genphy_config_aneg,
693 .read_status = genphy_read_status,
694 .ack_interrupt = bcm54xx_ack_interrupt,
695 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000696 .driver = { .owner = THIS_MODULE },
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100697};
698
699static struct phy_driver bcm5421_driver = {
700 .phy_id = 0x002060e0,
701 .phy_id_mask = 0xfffffff0,
702 .name = "Broadcom BCM5421",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800703 .features = PHY_GBIT_FEATURES |
704 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100705 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
706 .config_init = bcm54xx_config_init,
707 .config_aneg = genphy_config_aneg,
708 .read_status = genphy_read_status,
709 .ack_interrupt = bcm54xx_ack_interrupt,
710 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000711 .driver = { .owner = THIS_MODULE },
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100712};
713
714static struct phy_driver bcm5461_driver = {
715 .phy_id = 0x002060c0,
716 .phy_id_mask = 0xfffffff0,
717 .name = "Broadcom BCM5461",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800718 .features = PHY_GBIT_FEATURES |
719 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100720 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
721 .config_init = bcm54xx_config_init,
722 .config_aneg = genphy_config_aneg,
723 .read_status = genphy_read_status,
724 .ack_interrupt = bcm54xx_ack_interrupt,
725 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000726 .driver = { .owner = THIS_MODULE },
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100727};
728
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400729static struct phy_driver bcm5464_driver = {
730 .phy_id = 0x002060b0,
731 .phy_id_mask = 0xfffffff0,
732 .name = "Broadcom BCM5464",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800733 .features = PHY_GBIT_FEATURES |
734 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400735 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
736 .config_init = bcm54xx_config_init,
737 .config_aneg = genphy_config_aneg,
738 .read_status = genphy_read_status,
739 .ack_interrupt = bcm54xx_ack_interrupt,
740 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000741 .driver = { .owner = THIS_MODULE },
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400742};
743
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300744static struct phy_driver bcm5481_driver = {
745 .phy_id = 0x0143bca0,
746 .phy_id_mask = 0xfffffff0,
747 .name = "Broadcom BCM5481",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800748 .features = PHY_GBIT_FEATURES |
749 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300750 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
751 .config_init = bcm54xx_config_init,
752 .config_aneg = bcm5481_config_aneg,
753 .read_status = genphy_read_status,
754 .ack_interrupt = bcm54xx_ack_interrupt,
755 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000756 .driver = { .owner = THIS_MODULE },
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300757};
758
Nate Case03157ac2008-01-29 10:19:00 -0600759static struct phy_driver bcm5482_driver = {
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300760 .phy_id = 0x0143bcb0,
Nate Case03157ac2008-01-29 10:19:00 -0600761 .phy_id_mask = 0xfffffff0,
762 .name = "Broadcom BCM5482",
Matt Carlson5e0c6762008-11-03 16:56:07 -0800763 .features = PHY_GBIT_FEATURES |
764 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
Nate Case03157ac2008-01-29 10:19:00 -0600765 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Nate Casecd9af3d2008-05-17 06:40:39 +0100766 .config_init = bcm5482_config_init,
Nate Case03157ac2008-01-29 10:19:00 -0600767 .config_aneg = genphy_config_aneg,
Nate Casecd9af3d2008-05-17 06:40:39 +0100768 .read_status = bcm5482_read_status,
Nate Case03157ac2008-01-29 10:19:00 -0600769 .ack_interrupt = bcm54xx_ack_interrupt,
770 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000771 .driver = { .owner = THIS_MODULE },
Nate Case03157ac2008-01-29 10:19:00 -0600772};
773
Matt Carlson772638b2008-11-03 16:56:51 -0800774static struct phy_driver bcm50610_driver = {
775 .phy_id = PHY_ID_BCM50610,
776 .phy_id_mask = 0xfffffff0,
777 .name = "Broadcom BCM50610",
778 .features = PHY_GBIT_FEATURES |
779 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
780 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
781 .config_init = bcm54xx_config_init,
782 .config_aneg = genphy_config_aneg,
783 .read_status = genphy_read_status,
784 .ack_interrupt = bcm54xx_ack_interrupt,
785 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000786 .driver = { .owner = THIS_MODULE },
787};
788
789static struct phy_driver bcm50610m_driver = {
790 .phy_id = PHY_ID_BCM50610M,
791 .phy_id_mask = 0xfffffff0,
792 .name = "Broadcom BCM50610M",
793 .features = PHY_GBIT_FEATURES |
794 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
795 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
796 .config_init = bcm54xx_config_init,
797 .config_aneg = genphy_config_aneg,
798 .read_status = genphy_read_status,
799 .ack_interrupt = bcm54xx_ack_interrupt,
800 .config_intr = bcm54xx_config_intr,
801 .driver = { .owner = THIS_MODULE },
Matt Carlson772638b2008-11-03 16:56:51 -0800802};
803
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800804static struct phy_driver bcm57780_driver = {
Matt Carlsond9221e62009-08-25 10:11:26 +0000805 .phy_id = PHY_ID_BCM57780,
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800806 .phy_id_mask = 0xfffffff0,
807 .name = "Broadcom BCM57780",
808 .features = PHY_GBIT_FEATURES |
809 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
810 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
811 .config_init = bcm54xx_config_init,
812 .config_aneg = genphy_config_aneg,
813 .read_status = genphy_read_status,
814 .ack_interrupt = bcm54xx_ack_interrupt,
815 .config_intr = bcm54xx_config_intr,
Matt Carlson4f4598f2009-08-25 10:10:30 +0000816 .driver = { .owner = THIS_MODULE },
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800817};
818
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000819static struct phy_driver bcmac131_driver = {
820 .phy_id = 0x0143bc70,
821 .phy_id_mask = 0xfffffff0,
822 .name = "Broadcom BCMAC131",
823 .features = PHY_BASIC_FEATURES |
824 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
825 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
826 .config_init = brcm_fet_config_init,
827 .config_aneg = genphy_config_aneg,
828 .read_status = genphy_read_status,
829 .ack_interrupt = brcm_fet_ack_interrupt,
830 .config_intr = brcm_fet_config_intr,
831 .driver = { .owner = THIS_MODULE },
832};
833
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100834static int __init broadcom_init(void)
835{
836 int ret;
837
838 ret = phy_driver_register(&bcm5411_driver);
839 if (ret)
840 goto out_5411;
841 ret = phy_driver_register(&bcm5421_driver);
842 if (ret)
843 goto out_5421;
844 ret = phy_driver_register(&bcm5461_driver);
845 if (ret)
846 goto out_5461;
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400847 ret = phy_driver_register(&bcm5464_driver);
848 if (ret)
849 goto out_5464;
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300850 ret = phy_driver_register(&bcm5481_driver);
851 if (ret)
852 goto out_5481;
Nate Case03157ac2008-01-29 10:19:00 -0600853 ret = phy_driver_register(&bcm5482_driver);
854 if (ret)
855 goto out_5482;
Matt Carlson772638b2008-11-03 16:56:51 -0800856 ret = phy_driver_register(&bcm50610_driver);
857 if (ret)
858 goto out_50610;
Matt Carlson4f4598f2009-08-25 10:10:30 +0000859 ret = phy_driver_register(&bcm50610m_driver);
860 if (ret)
861 goto out_50610m;
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800862 ret = phy_driver_register(&bcm57780_driver);
863 if (ret)
864 goto out_57780;
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000865 ret = phy_driver_register(&bcmac131_driver);
866 if (ret)
867 goto out_ac131;
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100868 return ret;
869
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000870out_ac131:
871 phy_driver_unregister(&bcm57780_driver);
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800872out_57780:
Matt Carlson4f4598f2009-08-25 10:10:30 +0000873 phy_driver_unregister(&bcm50610m_driver);
874out_50610m:
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800875 phy_driver_unregister(&bcm50610_driver);
Matt Carlson772638b2008-11-03 16:56:51 -0800876out_50610:
877 phy_driver_unregister(&bcm5482_driver);
Nate Case03157ac2008-01-29 10:19:00 -0600878out_5482:
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300879 phy_driver_unregister(&bcm5481_driver);
880out_5481:
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400881 phy_driver_unregister(&bcm5464_driver);
882out_5464:
Nate Case03157ac2008-01-29 10:19:00 -0600883 phy_driver_unregister(&bcm5461_driver);
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100884out_5461:
885 phy_driver_unregister(&bcm5421_driver);
886out_5421:
887 phy_driver_unregister(&bcm5411_driver);
888out_5411:
889 return ret;
890}
891
892static void __exit broadcom_exit(void)
893{
Matt Carlsond7a2ed92009-08-25 10:10:58 +0000894 phy_driver_unregister(&bcmac131_driver);
Matt Carlson2fbb69a2008-11-21 17:22:53 -0800895 phy_driver_unregister(&bcm57780_driver);
Matt Carlson4f4598f2009-08-25 10:10:30 +0000896 phy_driver_unregister(&bcm50610m_driver);
Matt Carlson772638b2008-11-03 16:56:51 -0800897 phy_driver_unregister(&bcm50610_driver);
Nate Case03157ac2008-01-29 10:19:00 -0600898 phy_driver_unregister(&bcm5482_driver);
Anton Vorontsov57bb7e22008-03-04 19:41:32 +0300899 phy_driver_unregister(&bcm5481_driver);
Paul Gortmakerb1394f92008-04-14 23:35:41 -0400900 phy_driver_unregister(&bcm5464_driver);
Maciej W. Rozyckic4b41c92006-10-03 16:18:13 +0100901 phy_driver_unregister(&bcm5461_driver);
902 phy_driver_unregister(&bcm5421_driver);
903 phy_driver_unregister(&bcm5411_driver);
904}
905
906module_init(broadcom_init);
907module_exit(broadcom_exit);