xref: /openbmc/linux/drivers/net/phy/broadcom.c (revision f42b3800)
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_ISR		0x1a	/* BCM54xx interrupt status register */
28 #define MII_BCM54XX_IMR		0x1b	/* BCM54xx interrupt mask register */
29 #define MII_BCM54XX_INT_CRCERR	0x0001	/* CRC error */
30 #define MII_BCM54XX_INT_LINK	0x0002	/* Link status changed */
31 #define MII_BCM54XX_INT_SPEED	0x0004	/* Link speed change */
32 #define MII_BCM54XX_INT_DUPLEX	0x0008	/* Duplex mode changed */
33 #define MII_BCM54XX_INT_LRS	0x0010	/* Local receiver status changed */
34 #define MII_BCM54XX_INT_RRS	0x0020	/* Remote receiver status changed */
35 #define MII_BCM54XX_INT_SSERR	0x0040	/* Scrambler synchronization error */
36 #define MII_BCM54XX_INT_UHCD	0x0080	/* Unsupported HCD negotiated */
37 #define MII_BCM54XX_INT_NHCD	0x0100	/* No HCD */
38 #define MII_BCM54XX_INT_NHCDL	0x0200	/* No HCD link */
39 #define MII_BCM54XX_INT_ANPR	0x0400	/* Auto-negotiation page received */
40 #define MII_BCM54XX_INT_LC	0x0800	/* All counters below 128 */
41 #define MII_BCM54XX_INT_HC	0x1000	/* Counter above 32768 */
42 #define MII_BCM54XX_INT_MDIX	0x2000	/* MDIX status change */
43 #define MII_BCM54XX_INT_PSERR	0x4000	/* Pair swap error */
44 
45 MODULE_DESCRIPTION("Broadcom PHY driver");
46 MODULE_AUTHOR("Maciej W. Rozycki");
47 MODULE_LICENSE("GPL");
48 
49 static int bcm54xx_config_init(struct phy_device *phydev)
50 {
51 	int reg, err;
52 
53 	reg = phy_read(phydev, MII_BCM54XX_ECR);
54 	if (reg < 0)
55 		return reg;
56 
57 	/* Mask interrupts globally.  */
58 	reg |= MII_BCM54XX_ECR_IM;
59 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
60 	if (err < 0)
61 		return err;
62 
63 	/* Unmask events we are interested in.  */
64 	reg = ~(MII_BCM54XX_INT_DUPLEX |
65 		MII_BCM54XX_INT_SPEED |
66 		MII_BCM54XX_INT_LINK);
67 	err = phy_write(phydev, MII_BCM54XX_IMR, reg);
68 	if (err < 0)
69 		return err;
70 	return 0;
71 }
72 
73 static int bcm54xx_ack_interrupt(struct phy_device *phydev)
74 {
75 	int reg;
76 
77 	/* Clear pending interrupts.  */
78 	reg = phy_read(phydev, MII_BCM54XX_ISR);
79 	if (reg < 0)
80 		return reg;
81 
82 	return 0;
83 }
84 
85 static int bcm54xx_config_intr(struct phy_device *phydev)
86 {
87 	int reg, err;
88 
89 	reg = phy_read(phydev, MII_BCM54XX_ECR);
90 	if (reg < 0)
91 		return reg;
92 
93 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
94 		reg &= ~MII_BCM54XX_ECR_IM;
95 	else
96 		reg |= MII_BCM54XX_ECR_IM;
97 
98 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
99 	return err;
100 }
101 
102 static int bcm5481_config_aneg(struct phy_device *phydev)
103 {
104 	int ret;
105 
106 	/* Aneg firsly. */
107 	ret = genphy_config_aneg(phydev);
108 
109 	/* Then we can set up the delay. */
110 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
111 		u16 reg;
112 
113 		/*
114 		 * There is no BCM5481 specification available, so down
115 		 * here is everything we know about "register 0x18". This
116 		 * at least helps BCM5481 to successfuly receive packets
117 		 * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
118 		 * says: "This sets delay between the RXD and RXC signals
119 		 * instead of using trace lengths to achieve timing".
120 		 */
121 
122 		/* Set RDX clk delay. */
123 		reg = 0x7 | (0x7 << 12);
124 		phy_write(phydev, 0x18, reg);
125 
126 		reg = phy_read(phydev, 0x18);
127 		/* Set RDX-RXC skew. */
128 		reg |= (1 << 8);
129 		/* Write bits 14:0. */
130 		reg |= (1 << 15);
131 		phy_write(phydev, 0x18, reg);
132 	}
133 
134 	return ret;
135 }
136 
137 static struct phy_driver bcm5411_driver = {
138 	.phy_id		= 0x00206070,
139 	.phy_id_mask	= 0xfffffff0,
140 	.name		= "Broadcom BCM5411",
141 	.features	= PHY_GBIT_FEATURES,
142 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
143 	.config_init	= bcm54xx_config_init,
144 	.config_aneg	= genphy_config_aneg,
145 	.read_status	= genphy_read_status,
146 	.ack_interrupt	= bcm54xx_ack_interrupt,
147 	.config_intr	= bcm54xx_config_intr,
148 	.driver 	= { .owner = THIS_MODULE },
149 };
150 
151 static struct phy_driver bcm5421_driver = {
152 	.phy_id		= 0x002060e0,
153 	.phy_id_mask	= 0xfffffff0,
154 	.name		= "Broadcom BCM5421",
155 	.features	= PHY_GBIT_FEATURES,
156 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
157 	.config_init	= bcm54xx_config_init,
158 	.config_aneg	= genphy_config_aneg,
159 	.read_status	= genphy_read_status,
160 	.ack_interrupt	= bcm54xx_ack_interrupt,
161 	.config_intr	= bcm54xx_config_intr,
162 	.driver 	= { .owner = THIS_MODULE },
163 };
164 
165 static struct phy_driver bcm5461_driver = {
166 	.phy_id		= 0x002060c0,
167 	.phy_id_mask	= 0xfffffff0,
168 	.name		= "Broadcom BCM5461",
169 	.features	= PHY_GBIT_FEATURES,
170 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
171 	.config_init	= bcm54xx_config_init,
172 	.config_aneg	= genphy_config_aneg,
173 	.read_status	= genphy_read_status,
174 	.ack_interrupt	= bcm54xx_ack_interrupt,
175 	.config_intr	= bcm54xx_config_intr,
176 	.driver 	= { .owner = THIS_MODULE },
177 };
178 
179 static struct phy_driver bcm5464_driver = {
180 	.phy_id		= 0x002060b0,
181 	.phy_id_mask	= 0xfffffff0,
182 	.name		= "Broadcom BCM5464",
183 	.features	= PHY_GBIT_FEATURES,
184 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
185 	.config_init	= bcm54xx_config_init,
186 	.config_aneg	= genphy_config_aneg,
187 	.read_status	= genphy_read_status,
188 	.ack_interrupt	= bcm54xx_ack_interrupt,
189 	.config_intr	= bcm54xx_config_intr,
190 	.driver 	= { .owner = THIS_MODULE },
191 };
192 
193 static struct phy_driver bcm5481_driver = {
194 	.phy_id		= 0x0143bca0,
195 	.phy_id_mask	= 0xfffffff0,
196 	.name		= "Broadcom BCM5481",
197 	.features	= PHY_GBIT_FEATURES,
198 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
199 	.config_init	= bcm54xx_config_init,
200 	.config_aneg	= bcm5481_config_aneg,
201 	.read_status	= genphy_read_status,
202 	.ack_interrupt	= bcm54xx_ack_interrupt,
203 	.config_intr	= bcm54xx_config_intr,
204 	.driver 	= { .owner = THIS_MODULE },
205 };
206 
207 static struct phy_driver bcm5482_driver = {
208 	.phy_id		= 0x0143bcb0,
209 	.phy_id_mask	= 0xfffffff0,
210 	.name		= "Broadcom BCM5482",
211 	.features	= PHY_GBIT_FEATURES,
212 	.flags		= PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
213 	.config_init	= bcm54xx_config_init,
214 	.config_aneg	= genphy_config_aneg,
215 	.read_status	= genphy_read_status,
216 	.ack_interrupt	= bcm54xx_ack_interrupt,
217 	.config_intr	= bcm54xx_config_intr,
218 	.driver 	= { .owner = THIS_MODULE },
219 };
220 
221 static int __init broadcom_init(void)
222 {
223 	int ret;
224 
225 	ret = phy_driver_register(&bcm5411_driver);
226 	if (ret)
227 		goto out_5411;
228 	ret = phy_driver_register(&bcm5421_driver);
229 	if (ret)
230 		goto out_5421;
231 	ret = phy_driver_register(&bcm5461_driver);
232 	if (ret)
233 		goto out_5461;
234 	ret = phy_driver_register(&bcm5464_driver);
235 	if (ret)
236 		goto out_5464;
237 	ret = phy_driver_register(&bcm5481_driver);
238 	if (ret)
239 		goto out_5481;
240 	ret = phy_driver_register(&bcm5482_driver);
241 	if (ret)
242 		goto out_5482;
243 	return ret;
244 
245 out_5482:
246 	phy_driver_unregister(&bcm5481_driver);
247 out_5481:
248 	phy_driver_unregister(&bcm5464_driver);
249 out_5464:
250 	phy_driver_unregister(&bcm5461_driver);
251 out_5461:
252 	phy_driver_unregister(&bcm5421_driver);
253 out_5421:
254 	phy_driver_unregister(&bcm5411_driver);
255 out_5411:
256 	return ret;
257 }
258 
259 static void __exit broadcom_exit(void)
260 {
261 	phy_driver_unregister(&bcm5482_driver);
262 	phy_driver_unregister(&bcm5481_driver);
263 	phy_driver_unregister(&bcm5464_driver);
264 	phy_driver_unregister(&bcm5461_driver);
265 	phy_driver_unregister(&bcm5421_driver);
266 	phy_driver_unregister(&bcm5411_driver);
267 }
268 
269 module_init(broadcom_init);
270 module_exit(broadcom_exit);
271