| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 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 |  | 
 | 20 | #define MII_BCM54XX_ECR		0x10	/* BCM54xx extended control register */ | 
 | 21 | #define MII_BCM54XX_ECR_IM	0x1000	/* Interrupt mask */ | 
 | 22 | #define MII_BCM54XX_ECR_IF	0x0800	/* Interrupt force */ | 
 | 23 |  | 
 | 24 | #define MII_BCM54XX_ESR		0x11	/* BCM54xx extended status register */ | 
 | 25 | #define MII_BCM54XX_ESR_IS	0x1000	/* Interrupt status */ | 
 | 26 |  | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 27 | #define MII_BCM54XX_EXP_DATA	0x15	/* Expansion register data */ | 
 | 28 | #define MII_BCM54XX_EXP_SEL	0x17	/* Expansion register select */ | 
 | 29 | #define MII_BCM54XX_EXP_SEL_SSD	0x0e00	/* Secondary SerDes select */ | 
 | 30 | #define MII_BCM54XX_EXP_SEL_ER	0x0f00	/* Expansion register select */ | 
 | 31 |  | 
 | 32 | #define MII_BCM54XX_AUX_CTL	0x18	/* Auxiliary control register */ | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 33 | #define MII_BCM54XX_ISR		0x1a	/* BCM54xx interrupt status register */ | 
 | 34 | #define MII_BCM54XX_IMR		0x1b	/* BCM54xx interrupt mask register */ | 
 | 35 | #define MII_BCM54XX_INT_CRCERR	0x0001	/* CRC error */ | 
 | 36 | #define MII_BCM54XX_INT_LINK	0x0002	/* Link status changed */ | 
 | 37 | #define MII_BCM54XX_INT_SPEED	0x0004	/* Link speed change */ | 
 | 38 | #define MII_BCM54XX_INT_DUPLEX	0x0008	/* Duplex mode changed */ | 
 | 39 | #define MII_BCM54XX_INT_LRS	0x0010	/* Local receiver status changed */ | 
 | 40 | #define MII_BCM54XX_INT_RRS	0x0020	/* Remote receiver status changed */ | 
 | 41 | #define MII_BCM54XX_INT_SSERR	0x0040	/* Scrambler synchronization error */ | 
 | 42 | #define MII_BCM54XX_INT_UHCD	0x0080	/* Unsupported HCD negotiated */ | 
 | 43 | #define MII_BCM54XX_INT_NHCD	0x0100	/* No HCD */ | 
 | 44 | #define MII_BCM54XX_INT_NHCDL	0x0200	/* No HCD link */ | 
 | 45 | #define MII_BCM54XX_INT_ANPR	0x0400	/* Auto-negotiation page received */ | 
 | 46 | #define MII_BCM54XX_INT_LC	0x0800	/* All counters below 128 */ | 
 | 47 | #define MII_BCM54XX_INT_HC	0x1000	/* Counter above 32768 */ | 
 | 48 | #define MII_BCM54XX_INT_MDIX	0x2000	/* MDIX status change */ | 
 | 49 | #define MII_BCM54XX_INT_PSERR	0x4000	/* Pair swap error */ | 
 | 50 |  | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 51 | #define MII_BCM54XX_SHD		0x1c	/* 0x1c shadow registers */ | 
 | 52 | #define MII_BCM54XX_SHD_WRITE	0x8000 | 
 | 53 | #define MII_BCM54XX_SHD_VAL(x)	((x & 0x1f) << 10) | 
 | 54 | #define MII_BCM54XX_SHD_DATA(x)	((x & 0x3ff) << 0) | 
 | 55 |  | 
 | 56 | /* | 
 | 57 |  * Broadcom LED source encodings.  These are used in BCM5461, BCM5481, | 
 | 58 |  * BCM5482, and possibly some others. | 
 | 59 |  */ | 
 | 60 | #define BCM_LED_SRC_LINKSPD1	0x0 | 
 | 61 | #define BCM_LED_SRC_LINKSPD2	0x1 | 
 | 62 | #define BCM_LED_SRC_XMITLED	0x2 | 
 | 63 | #define BCM_LED_SRC_ACTIVITYLED	0x3 | 
 | 64 | #define BCM_LED_SRC_FDXLED	0x4 | 
 | 65 | #define BCM_LED_SRC_SLAVE	0x5 | 
 | 66 | #define BCM_LED_SRC_INTR	0x6 | 
 | 67 | #define BCM_LED_SRC_QUALITY	0x7 | 
 | 68 | #define BCM_LED_SRC_RCVLED	0x8 | 
 | 69 | #define BCM_LED_SRC_MULTICOLOR1	0xa | 
 | 70 | #define BCM_LED_SRC_OPENSHORT	0xb | 
 | 71 | #define BCM_LED_SRC_OFF		0xe	/* Tied high */ | 
 | 72 | #define BCM_LED_SRC_ON		0xf	/* Tied low */ | 
 | 73 |  | 
 | 74 | /* | 
 | 75 |  * BCM5482: Shadow registers | 
 | 76 |  * Shadow values go into bits [14:10] of register 0x1c to select a shadow | 
 | 77 |  * register to access. | 
 | 78 |  */ | 
 | 79 | #define BCM5482_SHD_LEDS1	0x0d	/* 01101: LED Selector 1 */ | 
 | 80 | 					/* LED3 / ~LINKSPD[2] selector */ | 
 | 81 | #define BCM5482_SHD_LEDS1_LED3(src)	((src & 0xf) << 4) | 
 | 82 | 					/* LED1 / ~LINKSPD[1] selector */ | 
 | 83 | #define BCM5482_SHD_LEDS1_LED1(src)	((src & 0xf) << 0) | 
 | 84 | #define BCM5482_SHD_SSD		0x14	/* 10100: Secondary SerDes control */ | 
 | 85 | #define BCM5482_SHD_SSD_LEDM	0x0008	/* SSD LED Mode enable */ | 
 | 86 | #define BCM5482_SHD_SSD_EN	0x0001	/* SSD enable */ | 
 | 87 | #define BCM5482_SHD_MODE	0x1f	/* 11111: Mode Control Register */ | 
 | 88 | #define BCM5482_SHD_MODE_1000BX	0x0001	/* Enable 1000BASE-X registers */ | 
 | 89 |  | 
 | 90 | /* | 
 | 91 |  * BCM5482: Secondary SerDes registers | 
 | 92 |  */ | 
 | 93 | #define BCM5482_SSD_1000BX_CTL		0x00	/* 1000BASE-X Control */ | 
 | 94 | #define BCM5482_SSD_1000BX_CTL_PWRDOWN	0x0800	/* Power-down SSD */ | 
 | 95 | #define BCM5482_SSD_SGMII_SLAVE		0x15	/* SGMII Slave Register */ | 
 | 96 | #define BCM5482_SSD_SGMII_SLAVE_EN	0x0002	/* Slave mode enable */ | 
 | 97 | #define BCM5482_SSD_SGMII_SLAVE_AD	0x0001	/* Slave auto-detection */ | 
 | 98 |  | 
 | 99 | /* | 
 | 100 |  * Device flags for PHYs that can be configured for different operating | 
 | 101 |  * modes. | 
 | 102 |  */ | 
 | 103 | #define PHY_BCM_FLAGS_VALID		0x80000000 | 
 | 104 | #define PHY_BCM_FLAGS_INTF_XAUI		0x00000020 | 
 | 105 | #define PHY_BCM_FLAGS_INTF_SGMII	0x00000010 | 
 | 106 | #define PHY_BCM_FLAGS_MODE_1000BX	0x00000002 | 
 | 107 | #define PHY_BCM_FLAGS_MODE_COPPER	0x00000001 | 
 | 108 |  | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 109 | MODULE_DESCRIPTION("Broadcom PHY driver"); | 
 | 110 | MODULE_AUTHOR("Maciej W. Rozycki"); | 
 | 111 | MODULE_LICENSE("GPL"); | 
 | 112 |  | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 113 | /* | 
 | 114 |  * Indirect register access functions for the 1000BASE-T/100BASE-TX/10BASE-T | 
 | 115 |  * 0x1c shadow registers. | 
 | 116 |  */ | 
 | 117 | static int bcm54xx_shadow_read(struct phy_device *phydev, u16 shadow) | 
 | 118 | { | 
 | 119 | 	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); | 
 | 120 | 	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); | 
 | 121 | } | 
 | 122 |  | 
 | 123 | static int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow, u16 val) | 
 | 124 | { | 
 | 125 | 	return phy_write(phydev, MII_BCM54XX_SHD, | 
 | 126 | 			 MII_BCM54XX_SHD_WRITE | | 
 | 127 | 			 MII_BCM54XX_SHD_VAL(shadow) | | 
 | 128 | 			 MII_BCM54XX_SHD_DATA(val)); | 
 | 129 | } | 
 | 130 |  | 
 | 131 | /* | 
 | 132 |  * Indirect register access functions for the Expansion Registers | 
 | 133 |  * and Secondary SerDes registers (when sec_serdes=1). | 
 | 134 |  */ | 
 | 135 | static int bcm54xx_exp_read(struct phy_device *phydev, | 
 | 136 | 			    int sec_serdes, u8 regnum) | 
 | 137 | { | 
 | 138 | 	int val; | 
 | 139 |  | 
 | 140 | 	phy_write(phydev, MII_BCM54XX_EXP_SEL, | 
 | 141 | 		  (sec_serdes ? MII_BCM54XX_EXP_SEL_SSD : | 
 | 142 | 				MII_BCM54XX_EXP_SEL_ER) | | 
 | 143 | 		  regnum); | 
 | 144 | 	val = phy_read(phydev, MII_BCM54XX_EXP_DATA); | 
 | 145 | 	phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); | 
 | 146 |  | 
 | 147 | 	return val; | 
 | 148 | } | 
 | 149 |  | 
 | 150 | static int bcm54xx_exp_write(struct phy_device *phydev, | 
 | 151 | 			     int sec_serdes, u8 regnum, u16 val) | 
 | 152 | { | 
 | 153 | 	int ret; | 
 | 154 |  | 
 | 155 | 	phy_write(phydev, MII_BCM54XX_EXP_SEL, | 
 | 156 | 		  (sec_serdes ? MII_BCM54XX_EXP_SEL_SSD : | 
 | 157 | 				MII_BCM54XX_EXP_SEL_ER) | | 
 | 158 | 		  regnum); | 
 | 159 | 	ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val); | 
 | 160 | 	phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum); | 
 | 161 |  | 
 | 162 | 	return ret; | 
 | 163 | } | 
 | 164 |  | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 165 | static int bcm54xx_config_init(struct phy_device *phydev) | 
 | 166 | { | 
 | 167 | 	int reg, err; | 
 | 168 |  | 
 | 169 | 	reg = phy_read(phydev, MII_BCM54XX_ECR); | 
 | 170 | 	if (reg < 0) | 
 | 171 | 		return reg; | 
 | 172 |  | 
 | 173 | 	/* Mask interrupts globally.  */ | 
 | 174 | 	reg |= MII_BCM54XX_ECR_IM; | 
 | 175 | 	err = phy_write(phydev, MII_BCM54XX_ECR, reg); | 
 | 176 | 	if (err < 0) | 
 | 177 | 		return err; | 
 | 178 |  | 
 | 179 | 	/* Unmask events we are interested in.  */ | 
 | 180 | 	reg = ~(MII_BCM54XX_INT_DUPLEX | | 
 | 181 | 		MII_BCM54XX_INT_SPEED | | 
 | 182 | 		MII_BCM54XX_INT_LINK); | 
 | 183 | 	err = phy_write(phydev, MII_BCM54XX_IMR, reg); | 
 | 184 | 	if (err < 0) | 
 | 185 | 		return err; | 
 | 186 | 	return 0; | 
 | 187 | } | 
 | 188 |  | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 189 | static int bcm5482_config_init(struct phy_device *phydev) | 
 | 190 | { | 
 | 191 | 	int err, reg; | 
 | 192 |  | 
 | 193 | 	err = bcm54xx_config_init(phydev); | 
 | 194 |  | 
 | 195 | 	if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) { | 
 | 196 | 		/* | 
 | 197 | 		 * Enable secondary SerDes and its use as an LED source | 
 | 198 | 		 */ | 
 | 199 | 		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD); | 
 | 200 | 		bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD, | 
 | 201 | 				     reg | | 
 | 202 | 				     BCM5482_SHD_SSD_LEDM | | 
 | 203 | 				     BCM5482_SHD_SSD_EN); | 
 | 204 |  | 
 | 205 | 		/* | 
 | 206 | 		 * Enable SGMII slave mode and auto-detection | 
 | 207 | 		 */ | 
 | 208 | 		reg = bcm54xx_exp_read(phydev, 1, BCM5482_SSD_SGMII_SLAVE); | 
 | 209 | 		bcm54xx_exp_write(phydev, 1, BCM5482_SSD_SGMII_SLAVE, | 
 | 210 | 				  reg | | 
 | 211 | 				  BCM5482_SSD_SGMII_SLAVE_EN | | 
 | 212 | 				  BCM5482_SSD_SGMII_SLAVE_AD); | 
 | 213 |  | 
 | 214 | 		/* | 
 | 215 | 		 * Disable secondary SerDes powerdown | 
 | 216 | 		 */ | 
 | 217 | 		reg = bcm54xx_exp_read(phydev, 1, BCM5482_SSD_1000BX_CTL); | 
 | 218 | 		bcm54xx_exp_write(phydev, 1, BCM5482_SSD_1000BX_CTL, | 
 | 219 | 				  reg & ~BCM5482_SSD_1000BX_CTL_PWRDOWN); | 
 | 220 |  | 
 | 221 | 		/* | 
 | 222 | 		 * Select 1000BASE-X register set (primary SerDes) | 
 | 223 | 		 */ | 
 | 224 | 		reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE); | 
 | 225 | 		bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE, | 
 | 226 | 				     reg | BCM5482_SHD_MODE_1000BX); | 
 | 227 |  | 
 | 228 | 		/* | 
 | 229 | 		 * LED1=ACTIVITYLED, LED3=LINKSPD[2] | 
 | 230 | 		 * (Use LED1 as secondary SerDes ACTIVITY LED) | 
 | 231 | 		 */ | 
 | 232 | 		bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1, | 
 | 233 | 			BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) | | 
 | 234 | 			BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2)); | 
 | 235 |  | 
 | 236 | 		/* | 
 | 237 | 		 * Auto-negotiation doesn't seem to work quite right | 
 | 238 | 		 * in this mode, so we disable it and force it to the | 
 | 239 | 		 * right speed/duplex setting.  Only 'link status' | 
 | 240 | 		 * is important. | 
 | 241 | 		 */ | 
 | 242 | 		phydev->autoneg = AUTONEG_DISABLE; | 
 | 243 | 		phydev->speed = SPEED_1000; | 
 | 244 | 		phydev->duplex = DUPLEX_FULL; | 
 | 245 | 	} | 
 | 246 |  | 
 | 247 | 	return err; | 
 | 248 | } | 
 | 249 |  | 
 | 250 | static int bcm5482_read_status(struct phy_device *phydev) | 
 | 251 | { | 
 | 252 | 	int err; | 
 | 253 |  | 
 | 254 | 	err = genphy_read_status(phydev); | 
 | 255 |  | 
 | 256 | 	if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) { | 
 | 257 | 		/* | 
 | 258 | 		 * Only link status matters for 1000Base-X mode, so force | 
 | 259 | 		 * 1000 Mbit/s full-duplex status | 
 | 260 | 		 */ | 
 | 261 | 		if (phydev->link) { | 
 | 262 | 			phydev->speed = SPEED_1000; | 
 | 263 | 			phydev->duplex = DUPLEX_FULL; | 
 | 264 | 		} | 
 | 265 | 	} | 
 | 266 |  | 
 | 267 | 	return err; | 
 | 268 | } | 
 | 269 |  | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 270 | static int bcm54xx_ack_interrupt(struct phy_device *phydev) | 
 | 271 | { | 
 | 272 | 	int reg; | 
 | 273 |  | 
 | 274 | 	/* Clear pending interrupts.  */ | 
 | 275 | 	reg = phy_read(phydev, MII_BCM54XX_ISR); | 
 | 276 | 	if (reg < 0) | 
 | 277 | 		return reg; | 
 | 278 |  | 
 | 279 | 	return 0; | 
 | 280 | } | 
 | 281 |  | 
 | 282 | static int bcm54xx_config_intr(struct phy_device *phydev) | 
 | 283 | { | 
 | 284 | 	int reg, err; | 
 | 285 |  | 
 | 286 | 	reg = phy_read(phydev, MII_BCM54XX_ECR); | 
 | 287 | 	if (reg < 0) | 
 | 288 | 		return reg; | 
 | 289 |  | 
 | 290 | 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) | 
 | 291 | 		reg &= ~MII_BCM54XX_ECR_IM; | 
 | 292 | 	else | 
 | 293 | 		reg |= MII_BCM54XX_ECR_IM; | 
 | 294 |  | 
 | 295 | 	err = phy_write(phydev, MII_BCM54XX_ECR, reg); | 
 | 296 | 	return err; | 
 | 297 | } | 
 | 298 |  | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 299 | static int bcm5481_config_aneg(struct phy_device *phydev) | 
 | 300 | { | 
 | 301 | 	int ret; | 
 | 302 |  | 
 | 303 | 	/* Aneg firsly. */ | 
 | 304 | 	ret = genphy_config_aneg(phydev); | 
 | 305 |  | 
 | 306 | 	/* Then we can set up the delay. */ | 
 | 307 | 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { | 
 | 308 | 		u16 reg; | 
 | 309 |  | 
 | 310 | 		/* | 
 | 311 | 		 * There is no BCM5481 specification available, so down | 
 | 312 | 		 * here is everything we know about "register 0x18". This | 
 | 313 | 		 * at least helps BCM5481 to successfuly receive packets | 
 | 314 | 		 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com> | 
 | 315 | 		 * says: "This sets delay between the RXD and RXC signals | 
 | 316 | 		 * instead of using trace lengths to achieve timing". | 
 | 317 | 		 */ | 
 | 318 |  | 
 | 319 | 		/* Set RDX clk delay. */ | 
 | 320 | 		reg = 0x7 | (0x7 << 12); | 
 | 321 | 		phy_write(phydev, 0x18, reg); | 
 | 322 |  | 
 | 323 | 		reg = phy_read(phydev, 0x18); | 
 | 324 | 		/* Set RDX-RXC skew. */ | 
 | 325 | 		reg |= (1 << 8); | 
 | 326 | 		/* Write bits 14:0. */ | 
 | 327 | 		reg |= (1 << 15); | 
 | 328 | 		phy_write(phydev, 0x18, reg); | 
 | 329 | 	} | 
 | 330 |  | 
 | 331 | 	return ret; | 
 | 332 | } | 
 | 333 |  | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 334 | static struct phy_driver bcm5411_driver = { | 
 | 335 | 	.phy_id		= 0x00206070, | 
 | 336 | 	.phy_id_mask	= 0xfffffff0, | 
 | 337 | 	.name		= "Broadcom BCM5411", | 
 | 338 | 	.features	= PHY_GBIT_FEATURES, | 
 | 339 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
 | 340 | 	.config_init	= bcm54xx_config_init, | 
 | 341 | 	.config_aneg	= genphy_config_aneg, | 
 | 342 | 	.read_status	= genphy_read_status, | 
 | 343 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 344 | 	.config_intr	= bcm54xx_config_intr, | 
 | 345 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 346 | }; | 
 | 347 |  | 
 | 348 | static struct phy_driver bcm5421_driver = { | 
 | 349 | 	.phy_id		= 0x002060e0, | 
 | 350 | 	.phy_id_mask	= 0xfffffff0, | 
 | 351 | 	.name		= "Broadcom BCM5421", | 
 | 352 | 	.features	= PHY_GBIT_FEATURES, | 
 | 353 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
 | 354 | 	.config_init	= bcm54xx_config_init, | 
 | 355 | 	.config_aneg	= genphy_config_aneg, | 
 | 356 | 	.read_status	= genphy_read_status, | 
 | 357 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 358 | 	.config_intr	= bcm54xx_config_intr, | 
 | 359 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 360 | }; | 
 | 361 |  | 
 | 362 | static struct phy_driver bcm5461_driver = { | 
 | 363 | 	.phy_id		= 0x002060c0, | 
 | 364 | 	.phy_id_mask	= 0xfffffff0, | 
 | 365 | 	.name		= "Broadcom BCM5461", | 
 | 366 | 	.features	= PHY_GBIT_FEATURES, | 
 | 367 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
 | 368 | 	.config_init	= bcm54xx_config_init, | 
 | 369 | 	.config_aneg	= genphy_config_aneg, | 
 | 370 | 	.read_status	= genphy_read_status, | 
 | 371 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 372 | 	.config_intr	= bcm54xx_config_intr, | 
 | 373 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 374 | }; | 
 | 375 |  | 
| Paul Gortmaker | b1394f9 | 2008-04-14 23:35:41 -0400 | [diff] [blame] | 376 | static struct phy_driver bcm5464_driver = { | 
 | 377 | 	.phy_id		= 0x002060b0, | 
 | 378 | 	.phy_id_mask	= 0xfffffff0, | 
 | 379 | 	.name		= "Broadcom BCM5464", | 
 | 380 | 	.features	= PHY_GBIT_FEATURES, | 
 | 381 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
 | 382 | 	.config_init	= bcm54xx_config_init, | 
 | 383 | 	.config_aneg	= genphy_config_aneg, | 
 | 384 | 	.read_status	= genphy_read_status, | 
 | 385 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 386 | 	.config_intr	= bcm54xx_config_intr, | 
 | 387 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 388 | }; | 
 | 389 |  | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 390 | static struct phy_driver bcm5481_driver = { | 
 | 391 | 	.phy_id		= 0x0143bca0, | 
 | 392 | 	.phy_id_mask	= 0xfffffff0, | 
 | 393 | 	.name		= "Broadcom BCM5481", | 
 | 394 | 	.features	= PHY_GBIT_FEATURES, | 
 | 395 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
 | 396 | 	.config_init	= bcm54xx_config_init, | 
 | 397 | 	.config_aneg	= bcm5481_config_aneg, | 
 | 398 | 	.read_status	= genphy_read_status, | 
 | 399 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 400 | 	.config_intr	= bcm54xx_config_intr, | 
 | 401 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 402 | }; | 
 | 403 |  | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 404 | static struct phy_driver bcm5482_driver = { | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 405 | 	.phy_id		= 0x0143bcb0, | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 406 | 	.phy_id_mask	= 0xfffffff0, | 
 | 407 | 	.name		= "Broadcom BCM5482", | 
 | 408 | 	.features	= PHY_GBIT_FEATURES, | 
 | 409 | 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 410 | 	.config_init	= bcm5482_config_init, | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 411 | 	.config_aneg	= genphy_config_aneg, | 
| Nate Case | cd9af3d | 2008-05-17 06:40:39 +0100 | [diff] [blame] | 412 | 	.read_status	= bcm5482_read_status, | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 413 | 	.ack_interrupt	= bcm54xx_ack_interrupt, | 
 | 414 | 	.config_intr	= bcm54xx_config_intr, | 
 | 415 | 	.driver 	= { .owner = THIS_MODULE }, | 
 | 416 | }; | 
 | 417 |  | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 418 | static int __init broadcom_init(void) | 
 | 419 | { | 
 | 420 | 	int ret; | 
 | 421 |  | 
 | 422 | 	ret = phy_driver_register(&bcm5411_driver); | 
 | 423 | 	if (ret) | 
 | 424 | 		goto out_5411; | 
 | 425 | 	ret = phy_driver_register(&bcm5421_driver); | 
 | 426 | 	if (ret) | 
 | 427 | 		goto out_5421; | 
 | 428 | 	ret = phy_driver_register(&bcm5461_driver); | 
 | 429 | 	if (ret) | 
 | 430 | 		goto out_5461; | 
| Paul Gortmaker | b1394f9 | 2008-04-14 23:35:41 -0400 | [diff] [blame] | 431 | 	ret = phy_driver_register(&bcm5464_driver); | 
 | 432 | 	if (ret) | 
 | 433 | 		goto out_5464; | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 434 | 	ret = phy_driver_register(&bcm5481_driver); | 
 | 435 | 	if (ret) | 
 | 436 | 		goto out_5481; | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 437 | 	ret = phy_driver_register(&bcm5482_driver); | 
 | 438 | 	if (ret) | 
 | 439 | 		goto out_5482; | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 440 | 	return ret; | 
 | 441 |  | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 442 | out_5482: | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 443 | 	phy_driver_unregister(&bcm5481_driver); | 
 | 444 | out_5481: | 
| Paul Gortmaker | b1394f9 | 2008-04-14 23:35:41 -0400 | [diff] [blame] | 445 | 	phy_driver_unregister(&bcm5464_driver); | 
 | 446 | out_5464: | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 447 | 	phy_driver_unregister(&bcm5461_driver); | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 448 | out_5461: | 
 | 449 | 	phy_driver_unregister(&bcm5421_driver); | 
 | 450 | out_5421: | 
 | 451 | 	phy_driver_unregister(&bcm5411_driver); | 
 | 452 | out_5411: | 
 | 453 | 	return ret; | 
 | 454 | } | 
 | 455 |  | 
 | 456 | static void __exit broadcom_exit(void) | 
 | 457 | { | 
| Nate Case | 03157ac | 2008-01-29 10:19:00 -0600 | [diff] [blame] | 458 | 	phy_driver_unregister(&bcm5482_driver); | 
| Anton Vorontsov | 57bb7e2 | 2008-03-04 19:41:32 +0300 | [diff] [blame] | 459 | 	phy_driver_unregister(&bcm5481_driver); | 
| Paul Gortmaker | b1394f9 | 2008-04-14 23:35:41 -0400 | [diff] [blame] | 460 | 	phy_driver_unregister(&bcm5464_driver); | 
| Maciej W. Rozycki | c4b41c9 | 2006-10-03 16:18:13 +0100 | [diff] [blame] | 461 | 	phy_driver_unregister(&bcm5461_driver); | 
 | 462 | 	phy_driver_unregister(&bcm5421_driver); | 
 | 463 | 	phy_driver_unregister(&bcm5411_driver); | 
 | 464 | } | 
 | 465 |  | 
 | 466 | module_init(broadcom_init); | 
 | 467 | module_exit(broadcom_exit); |