xref: /openbmc/linux/drivers/net/phy/broadcom.c (revision 8fa5723aa7e053d498336b48448b292fc2e0458b)
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 
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 */
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 
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 
109 MODULE_DESCRIPTION("Broadcom PHY driver");
110 MODULE_AUTHOR("Maciej W. Rozycki");
111 MODULE_LICENSE("GPL");
112 
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 
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 
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 
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 
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 
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 
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 
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 
404 static struct phy_driver bcm5482_driver = {
405 	.phy_id		= 0x0143bcb0,
406 	.phy_id_mask	= 0xfffffff0,
407 	.name		= "Broadcom BCM5482",
408 	.features	= PHY_GBIT_FEATURES,
409 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
410 	.config_init	= bcm5482_config_init,
411 	.config_aneg	= genphy_config_aneg,
412 	.read_status	= bcm5482_read_status,
413 	.ack_interrupt	= bcm54xx_ack_interrupt,
414 	.config_intr	= bcm54xx_config_intr,
415 	.driver 	= { .owner = THIS_MODULE },
416 };
417 
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;
431 	ret = phy_driver_register(&bcm5464_driver);
432 	if (ret)
433 		goto out_5464;
434 	ret = phy_driver_register(&bcm5481_driver);
435 	if (ret)
436 		goto out_5481;
437 	ret = phy_driver_register(&bcm5482_driver);
438 	if (ret)
439 		goto out_5482;
440 	return ret;
441 
442 out_5482:
443 	phy_driver_unregister(&bcm5481_driver);
444 out_5481:
445 	phy_driver_unregister(&bcm5464_driver);
446 out_5464:
447 	phy_driver_unregister(&bcm5461_driver);
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 {
458 	phy_driver_unregister(&bcm5482_driver);
459 	phy_driver_unregister(&bcm5481_driver);
460 	phy_driver_unregister(&bcm5464_driver);
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);
468