xref: /rk3399_rockchip-uboot/drivers/net/phy/broadcom.c (revision 9082eeac5de1335d663016668c9b89c290f5c79b)
1*9082eeacSAndy Fleming /*
2*9082eeacSAndy Fleming  * Broadcom PHY drivers
3*9082eeacSAndy Fleming  *
4*9082eeacSAndy Fleming  * This program is free software; you can redistribute it and/or
5*9082eeacSAndy Fleming  * modify it under the terms of the GNU General Public License as
6*9082eeacSAndy Fleming  * published by the Free Software Foundation; either version 2 of
7*9082eeacSAndy Fleming  * the License, or (at your option) any later version.
8*9082eeacSAndy Fleming  *
9*9082eeacSAndy Fleming  * This program is distributed in the hope that it will be useful,
10*9082eeacSAndy Fleming  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*9082eeacSAndy Fleming  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*9082eeacSAndy Fleming  * GNU General Public License for more details.
13*9082eeacSAndy Fleming  *
14*9082eeacSAndy Fleming  * You should have received a copy of the GNU General Public License
15*9082eeacSAndy Fleming  * along with this program; if not, write to the Free Software
16*9082eeacSAndy Fleming  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17*9082eeacSAndy Fleming  * MA 02111-1307 USA
18*9082eeacSAndy Fleming  *
19*9082eeacSAndy Fleming  * Copyright 2010-2011 Freescale Semiconductor, Inc.
20*9082eeacSAndy Fleming  * author Andy Fleming
21*9082eeacSAndy Fleming  *
22*9082eeacSAndy Fleming  */
23*9082eeacSAndy Fleming #include <config.h>
24*9082eeacSAndy Fleming #include <common.h>
25*9082eeacSAndy Fleming #include <phy.h>
26*9082eeacSAndy Fleming 
27*9082eeacSAndy Fleming /* Broadcom BCM54xx -- taken from linux sungem_phy */
28*9082eeacSAndy Fleming #define MIIM_BCM54xx_AUXCNTL			0x18
29*9082eeacSAndy Fleming #define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
30*9082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS			0x19
31*9082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK	0x0700
32*9082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT	8
33*9082eeacSAndy Fleming 
34*9082eeacSAndy Fleming #define MIIM_BCM54XX_SHD			0x1c
35*9082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_WRITE			0x8000
36*9082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_VAL(x)			((x & 0x1f) << 10)
37*9082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_DATA(x)		((x & 0x3ff) << 0)
38*9082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_WR_ENCODE(val, data)	\
39*9082eeacSAndy Fleming 	(MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
40*9082eeacSAndy Fleming 	 MIIM_BCM54XX_SHD_DATA(data))
41*9082eeacSAndy Fleming 
42*9082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_DATA		0x15	/* Expansion register data */
43*9082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL		0x17	/* Expansion register select */
44*9082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL_SSD	0x0e00	/* Secondary SerDes select */
45*9082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL_ER		0x0f00	/* Expansion register select */
46*9082eeacSAndy Fleming 
47*9082eeacSAndy Fleming /* Broadcom BCM5461S */
48*9082eeacSAndy Fleming static int bcm5461_config(struct phy_device *phydev)
49*9082eeacSAndy Fleming {
50*9082eeacSAndy Fleming 	genphy_config_aneg(phydev);
51*9082eeacSAndy Fleming 
52*9082eeacSAndy Fleming 	phy_reset(phydev);
53*9082eeacSAndy Fleming 
54*9082eeacSAndy Fleming 	return 0;
55*9082eeacSAndy Fleming }
56*9082eeacSAndy Fleming 
57*9082eeacSAndy Fleming static int bcm54xx_parse_status(struct phy_device *phydev)
58*9082eeacSAndy Fleming {
59*9082eeacSAndy Fleming 	unsigned int mii_reg;
60*9082eeacSAndy Fleming 
61*9082eeacSAndy Fleming 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
62*9082eeacSAndy Fleming 
63*9082eeacSAndy Fleming 	switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
64*9082eeacSAndy Fleming 			MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
65*9082eeacSAndy Fleming 	case 1:
66*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
67*9082eeacSAndy Fleming 		phydev->speed = SPEED_10;
68*9082eeacSAndy Fleming 		break;
69*9082eeacSAndy Fleming 	case 2:
70*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
71*9082eeacSAndy Fleming 		phydev->speed = SPEED_10;
72*9082eeacSAndy Fleming 		break;
73*9082eeacSAndy Fleming 	case 3:
74*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
75*9082eeacSAndy Fleming 		phydev->speed = SPEED_100;
76*9082eeacSAndy Fleming 		break;
77*9082eeacSAndy Fleming 	case 5:
78*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
79*9082eeacSAndy Fleming 		phydev->speed = SPEED_100;
80*9082eeacSAndy Fleming 		break;
81*9082eeacSAndy Fleming 	case 6:
82*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
83*9082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
84*9082eeacSAndy Fleming 		break;
85*9082eeacSAndy Fleming 	case 7:
86*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
87*9082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
88*9082eeacSAndy Fleming 		break;
89*9082eeacSAndy Fleming 	default:
90*9082eeacSAndy Fleming 		printf("Auto-neg error, defaulting to 10BT/HD\n");
91*9082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
92*9082eeacSAndy Fleming 		phydev->speed = SPEED_10;
93*9082eeacSAndy Fleming 		break;
94*9082eeacSAndy Fleming 	}
95*9082eeacSAndy Fleming 
96*9082eeacSAndy Fleming 	return 0;
97*9082eeacSAndy Fleming }
98*9082eeacSAndy Fleming 
99*9082eeacSAndy Fleming static int bcm54xx_startup(struct phy_device *phydev)
100*9082eeacSAndy Fleming {
101*9082eeacSAndy Fleming 	/* Read the Status (2x to make sure link is right) */
102*9082eeacSAndy Fleming 	genphy_update_link(phydev);
103*9082eeacSAndy Fleming 	bcm54xx_parse_status(phydev);
104*9082eeacSAndy Fleming 
105*9082eeacSAndy Fleming 	return 0;
106*9082eeacSAndy Fleming }
107*9082eeacSAndy Fleming 
108*9082eeacSAndy Fleming /* Broadcom BCM5482S */
109*9082eeacSAndy Fleming /*
110*9082eeacSAndy Fleming  * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
111*9082eeacSAndy Fleming  * circumstances.  eg a gigabit TSEC connected to a gigabit switch with
112*9082eeacSAndy Fleming  * a 4-wire ethernet cable.  Both ends advertise gigabit, but can't
113*9082eeacSAndy Fleming  * link.  "Ethernet@Wirespeed" reduces advertised speed until link
114*9082eeacSAndy Fleming  * can be achieved.
115*9082eeacSAndy Fleming  */
116*9082eeacSAndy Fleming static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
117*9082eeacSAndy Fleming {
118*9082eeacSAndy Fleming 	return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
119*9082eeacSAndy Fleming }
120*9082eeacSAndy Fleming 
121*9082eeacSAndy Fleming static int bcm5482_config(struct phy_device *phydev)
122*9082eeacSAndy Fleming {
123*9082eeacSAndy Fleming 	unsigned int reg;
124*9082eeacSAndy Fleming 
125*9082eeacSAndy Fleming 	/* reset the PHY */
126*9082eeacSAndy Fleming 	reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
127*9082eeacSAndy Fleming 	reg |= BMCR_RESET;
128*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
129*9082eeacSAndy Fleming 
130*9082eeacSAndy Fleming 	/* Setup read from auxilary control shadow register 7 */
131*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
132*9082eeacSAndy Fleming 			MIIM_BCM54xx_AUXCNTL_ENCODE(7));
133*9082eeacSAndy Fleming 	/* Read Misc Control register and or in Ethernet@Wirespeed */
134*9082eeacSAndy Fleming 	reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
135*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
136*9082eeacSAndy Fleming 
137*9082eeacSAndy Fleming 	/* Initial config/enable of secondary SerDes interface */
138*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
139*9082eeacSAndy Fleming 			MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
140*9082eeacSAndy Fleming 	/* Write intial value to secondary SerDes Contol */
141*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
142*9082eeacSAndy Fleming 			MIIM_BCM54XX_EXP_SEL_SSD | 0);
143*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
144*9082eeacSAndy Fleming 			BMCR_ANRESTART);
145*9082eeacSAndy Fleming 	/* Enable copper/fiber auto-detect */
146*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
147*9082eeacSAndy Fleming 			MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
148*9082eeacSAndy Fleming 
149*9082eeacSAndy Fleming 	genphy_config_aneg(phydev);
150*9082eeacSAndy Fleming 
151*9082eeacSAndy Fleming 	return 0;
152*9082eeacSAndy Fleming }
153*9082eeacSAndy Fleming 
154*9082eeacSAndy Fleming /*
155*9082eeacSAndy Fleming  * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
156*9082eeacSAndy Fleming  * 0x42 - "Operating Mode Status Register"
157*9082eeacSAndy Fleming  */
158*9082eeacSAndy Fleming static int bcm5482_is_serdes(struct phy_device *phydev)
159*9082eeacSAndy Fleming {
160*9082eeacSAndy Fleming 	u16 val;
161*9082eeacSAndy Fleming 	int serdes = 0;
162*9082eeacSAndy Fleming 
163*9082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
164*9082eeacSAndy Fleming 			MIIM_BCM54XX_EXP_SEL_ER | 0x42);
165*9082eeacSAndy Fleming 	val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
166*9082eeacSAndy Fleming 
167*9082eeacSAndy Fleming 	switch (val & 0x1f) {
168*9082eeacSAndy Fleming 	case 0x0d:	/* RGMII-to-100Base-FX */
169*9082eeacSAndy Fleming 	case 0x0e:	/* RGMII-to-SGMII */
170*9082eeacSAndy Fleming 	case 0x0f:	/* RGMII-to-SerDes */
171*9082eeacSAndy Fleming 	case 0x12:	/* SGMII-to-SerDes */
172*9082eeacSAndy Fleming 	case 0x13:	/* SGMII-to-100Base-FX */
173*9082eeacSAndy Fleming 	case 0x16:	/* SerDes-to-Serdes */
174*9082eeacSAndy Fleming 		serdes = 1;
175*9082eeacSAndy Fleming 		break;
176*9082eeacSAndy Fleming 	case 0x6:	/* RGMII-to-Copper */
177*9082eeacSAndy Fleming 	case 0x14:	/* SGMII-to-Copper */
178*9082eeacSAndy Fleming 	case 0x17:	/* SerDes-to-Copper */
179*9082eeacSAndy Fleming 		break;
180*9082eeacSAndy Fleming 	default:
181*9082eeacSAndy Fleming 		printf("ERROR, invalid PHY mode (0x%x\n)", val);
182*9082eeacSAndy Fleming 		break;
183*9082eeacSAndy Fleming 	}
184*9082eeacSAndy Fleming 
185*9082eeacSAndy Fleming 	return serdes;
186*9082eeacSAndy Fleming }
187*9082eeacSAndy Fleming 
188*9082eeacSAndy Fleming /*
189*9082eeacSAndy Fleming  * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
190*9082eeacSAndy Fleming  * Mode Status Register"
191*9082eeacSAndy Fleming  */
192*9082eeacSAndy Fleming static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
193*9082eeacSAndy Fleming {
194*9082eeacSAndy Fleming 	u16 val;
195*9082eeacSAndy Fleming 	int i = 0;
196*9082eeacSAndy Fleming 
197*9082eeacSAndy Fleming 	/* Wait 1s for link - Clause 37 autonegotiation happens very fast */
198*9082eeacSAndy Fleming 	while (1) {
199*9082eeacSAndy Fleming 		phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
200*9082eeacSAndy Fleming 				MIIM_BCM54XX_EXP_SEL_ER | 0x42);
201*9082eeacSAndy Fleming 		val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
202*9082eeacSAndy Fleming 
203*9082eeacSAndy Fleming 		if (val & 0x8000)
204*9082eeacSAndy Fleming 			break;
205*9082eeacSAndy Fleming 
206*9082eeacSAndy Fleming 		if (i++ > 1000) {
207*9082eeacSAndy Fleming 			phydev->link = 0;
208*9082eeacSAndy Fleming 			return 1;
209*9082eeacSAndy Fleming 		}
210*9082eeacSAndy Fleming 
211*9082eeacSAndy Fleming 		udelay(1000);	/* 1 ms */
212*9082eeacSAndy Fleming 	}
213*9082eeacSAndy Fleming 
214*9082eeacSAndy Fleming 	phydev->link = 1;
215*9082eeacSAndy Fleming 	switch ((val >> 13) & 0x3) {
216*9082eeacSAndy Fleming 	case (0x00):
217*9082eeacSAndy Fleming 		phydev->speed = 10;
218*9082eeacSAndy Fleming 		break;
219*9082eeacSAndy Fleming 	case (0x01):
220*9082eeacSAndy Fleming 		phydev->speed = 100;
221*9082eeacSAndy Fleming 		break;
222*9082eeacSAndy Fleming 	case (0x02):
223*9082eeacSAndy Fleming 		phydev->speed = 1000;
224*9082eeacSAndy Fleming 		break;
225*9082eeacSAndy Fleming 	}
226*9082eeacSAndy Fleming 
227*9082eeacSAndy Fleming 	phydev->duplex = (val & 0x1000) == 0x1000;
228*9082eeacSAndy Fleming 
229*9082eeacSAndy Fleming 	return 0;
230*9082eeacSAndy Fleming }
231*9082eeacSAndy Fleming 
232*9082eeacSAndy Fleming /*
233*9082eeacSAndy Fleming  * Figure out if BCM5482 is in serdes or copper mode and determine link
234*9082eeacSAndy Fleming  * configuration accordingly
235*9082eeacSAndy Fleming  */
236*9082eeacSAndy Fleming static int bcm5482_startup(struct phy_device *phydev)
237*9082eeacSAndy Fleming {
238*9082eeacSAndy Fleming 	if (bcm5482_is_serdes(phydev)) {
239*9082eeacSAndy Fleming 		bcm5482_parse_serdes_sr(phydev);
240*9082eeacSAndy Fleming 		phydev->port = PORT_FIBRE;
241*9082eeacSAndy Fleming 	} else {
242*9082eeacSAndy Fleming 		/* Wait for auto-negotiation to complete or fail */
243*9082eeacSAndy Fleming 		genphy_update_link(phydev);
244*9082eeacSAndy Fleming 		/* Parse BCM54xx copper aux status register */
245*9082eeacSAndy Fleming 		bcm54xx_parse_status(phydev);
246*9082eeacSAndy Fleming 	}
247*9082eeacSAndy Fleming 
248*9082eeacSAndy Fleming 	return 0;
249*9082eeacSAndy Fleming }
250*9082eeacSAndy Fleming 
251*9082eeacSAndy Fleming static struct phy_driver BCM5461S_driver = {
252*9082eeacSAndy Fleming 	.name = "Broadcom BCM5461S",
253*9082eeacSAndy Fleming 	.uid = 0x2060c0,
254*9082eeacSAndy Fleming 	.mask = 0xfffff0,
255*9082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
256*9082eeacSAndy Fleming 	.config = &bcm5461_config,
257*9082eeacSAndy Fleming 	.startup = &bcm54xx_startup,
258*9082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
259*9082eeacSAndy Fleming };
260*9082eeacSAndy Fleming 
261*9082eeacSAndy Fleming static struct phy_driver BCM5464S_driver = {
262*9082eeacSAndy Fleming 	.name = "Broadcom BCM5464S",
263*9082eeacSAndy Fleming 	.uid = 0x2060b0,
264*9082eeacSAndy Fleming 	.mask = 0xfffff0,
265*9082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
266*9082eeacSAndy Fleming 	.config = &bcm5461_config,
267*9082eeacSAndy Fleming 	.startup = &bcm54xx_startup,
268*9082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
269*9082eeacSAndy Fleming };
270*9082eeacSAndy Fleming 
271*9082eeacSAndy Fleming static struct phy_driver BCM5482S_driver = {
272*9082eeacSAndy Fleming 	.name = "Broadcom BCM5482S",
273*9082eeacSAndy Fleming 	.uid = 0x143bcb0,
274*9082eeacSAndy Fleming 	.mask = 0xffffff0,
275*9082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
276*9082eeacSAndy Fleming 	.config = &bcm5482_config,
277*9082eeacSAndy Fleming 	.startup = &bcm5482_startup,
278*9082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
279*9082eeacSAndy Fleming };
280*9082eeacSAndy Fleming 
281*9082eeacSAndy Fleming int phy_broadcom_init(void)
282*9082eeacSAndy Fleming {
283*9082eeacSAndy Fleming 	phy_register(&BCM5482S_driver);
284*9082eeacSAndy Fleming 	phy_register(&BCM5464S_driver);
285*9082eeacSAndy Fleming 	phy_register(&BCM5461S_driver);
286*9082eeacSAndy Fleming 
287*9082eeacSAndy Fleming 	return 0;
288*9082eeacSAndy Fleming }
289