xref: /rk3399_rockchip-uboot/drivers/net/phy/broadcom.c (revision 1b564cecc358ccd08691c879fca95c2075fcb702)
19082eeacSAndy Fleming /*
29082eeacSAndy Fleming  * Broadcom PHY drivers
39082eeacSAndy Fleming  *
41a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
59082eeacSAndy Fleming  *
69082eeacSAndy Fleming  * Copyright 2010-2011 Freescale Semiconductor, Inc.
79082eeacSAndy Fleming  * author Andy Fleming
89082eeacSAndy Fleming  */
99082eeacSAndy Fleming #include <config.h>
109082eeacSAndy Fleming #include <common.h>
119082eeacSAndy Fleming #include <phy.h>
129082eeacSAndy Fleming 
139082eeacSAndy Fleming /* Broadcom BCM54xx -- taken from linux sungem_phy */
149082eeacSAndy Fleming #define MIIM_BCM54xx_AUXCNTL			0x18
159082eeacSAndy Fleming #define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
169082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS			0x19
179082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK	0x0700
189082eeacSAndy Fleming #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT	8
199082eeacSAndy Fleming 
209082eeacSAndy Fleming #define MIIM_BCM54XX_SHD			0x1c
219082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_WRITE			0x8000
229082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_VAL(x)			((x & 0x1f) << 10)
239082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_DATA(x)		((x & 0x3ff) << 0)
249082eeacSAndy Fleming #define MIIM_BCM54XX_SHD_WR_ENCODE(val, data)	\
259082eeacSAndy Fleming 	(MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
269082eeacSAndy Fleming 	 MIIM_BCM54XX_SHD_DATA(data))
279082eeacSAndy Fleming 
289082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_DATA		0x15	/* Expansion register data */
299082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL		0x17	/* Expansion register select */
309082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL_SSD	0x0e00	/* Secondary SerDes select */
319082eeacSAndy Fleming #define MIIM_BCM54XX_EXP_SEL_ER		0x0f00	/* Expansion register select */
329082eeacSAndy Fleming 
339082eeacSAndy Fleming /* Broadcom BCM5461S */
349082eeacSAndy Fleming static int bcm5461_config(struct phy_device *phydev)
359082eeacSAndy Fleming {
369082eeacSAndy Fleming 	genphy_config_aneg(phydev);
379082eeacSAndy Fleming 
389082eeacSAndy Fleming 	phy_reset(phydev);
399082eeacSAndy Fleming 
409082eeacSAndy Fleming 	return 0;
419082eeacSAndy Fleming }
429082eeacSAndy Fleming 
439082eeacSAndy Fleming static int bcm54xx_parse_status(struct phy_device *phydev)
449082eeacSAndy Fleming {
459082eeacSAndy Fleming 	unsigned int mii_reg;
469082eeacSAndy Fleming 
479082eeacSAndy Fleming 	mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
489082eeacSAndy Fleming 
499082eeacSAndy Fleming 	switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
509082eeacSAndy Fleming 			MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
519082eeacSAndy Fleming 	case 1:
529082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
539082eeacSAndy Fleming 		phydev->speed = SPEED_10;
549082eeacSAndy Fleming 		break;
559082eeacSAndy Fleming 	case 2:
569082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
579082eeacSAndy Fleming 		phydev->speed = SPEED_10;
589082eeacSAndy Fleming 		break;
599082eeacSAndy Fleming 	case 3:
609082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
619082eeacSAndy Fleming 		phydev->speed = SPEED_100;
629082eeacSAndy Fleming 		break;
639082eeacSAndy Fleming 	case 5:
649082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
659082eeacSAndy Fleming 		phydev->speed = SPEED_100;
669082eeacSAndy Fleming 		break;
679082eeacSAndy Fleming 	case 6:
689082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
699082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
709082eeacSAndy Fleming 		break;
719082eeacSAndy Fleming 	case 7:
729082eeacSAndy Fleming 		phydev->duplex = DUPLEX_FULL;
739082eeacSAndy Fleming 		phydev->speed = SPEED_1000;
749082eeacSAndy Fleming 		break;
759082eeacSAndy Fleming 	default:
769082eeacSAndy Fleming 		printf("Auto-neg error, defaulting to 10BT/HD\n");
779082eeacSAndy Fleming 		phydev->duplex = DUPLEX_HALF;
789082eeacSAndy Fleming 		phydev->speed = SPEED_10;
799082eeacSAndy Fleming 		break;
809082eeacSAndy Fleming 	}
819082eeacSAndy Fleming 
829082eeacSAndy Fleming 	return 0;
839082eeacSAndy Fleming }
849082eeacSAndy Fleming 
859082eeacSAndy Fleming static int bcm54xx_startup(struct phy_device *phydev)
869082eeacSAndy Fleming {
879082eeacSAndy Fleming 	/* Read the Status (2x to make sure link is right) */
889082eeacSAndy Fleming 	genphy_update_link(phydev);
899082eeacSAndy Fleming 	bcm54xx_parse_status(phydev);
909082eeacSAndy Fleming 
919082eeacSAndy Fleming 	return 0;
929082eeacSAndy Fleming }
939082eeacSAndy Fleming 
949082eeacSAndy Fleming /* Broadcom BCM5482S */
959082eeacSAndy Fleming /*
969082eeacSAndy Fleming  * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
979082eeacSAndy Fleming  * circumstances.  eg a gigabit TSEC connected to a gigabit switch with
989082eeacSAndy Fleming  * a 4-wire ethernet cable.  Both ends advertise gigabit, but can't
999082eeacSAndy Fleming  * link.  "Ethernet@Wirespeed" reduces advertised speed until link
1009082eeacSAndy Fleming  * can be achieved.
1019082eeacSAndy Fleming  */
1029082eeacSAndy Fleming static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
1039082eeacSAndy Fleming {
1049082eeacSAndy Fleming 	return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
1059082eeacSAndy Fleming }
1069082eeacSAndy Fleming 
1079082eeacSAndy Fleming static int bcm5482_config(struct phy_device *phydev)
1089082eeacSAndy Fleming {
1099082eeacSAndy Fleming 	unsigned int reg;
1109082eeacSAndy Fleming 
1119082eeacSAndy Fleming 	/* reset the PHY */
1129082eeacSAndy Fleming 	reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
1139082eeacSAndy Fleming 	reg |= BMCR_RESET;
1149082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
1159082eeacSAndy Fleming 
1169082eeacSAndy Fleming 	/* Setup read from auxilary control shadow register 7 */
1179082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
1189082eeacSAndy Fleming 			MIIM_BCM54xx_AUXCNTL_ENCODE(7));
1199082eeacSAndy Fleming 	/* Read Misc Control register and or in Ethernet@Wirespeed */
1209082eeacSAndy Fleming 	reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
1219082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
1229082eeacSAndy Fleming 
1239082eeacSAndy Fleming 	/* Initial config/enable of secondary SerDes interface */
1249082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
1259082eeacSAndy Fleming 			MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
1269082eeacSAndy Fleming 	/* Write intial value to secondary SerDes Contol */
1279082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
1289082eeacSAndy Fleming 			MIIM_BCM54XX_EXP_SEL_SSD | 0);
1299082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
1309082eeacSAndy Fleming 			BMCR_ANRESTART);
1319082eeacSAndy Fleming 	/* Enable copper/fiber auto-detect */
1329082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
1339082eeacSAndy Fleming 			MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
1349082eeacSAndy Fleming 
1359082eeacSAndy Fleming 	genphy_config_aneg(phydev);
1369082eeacSAndy Fleming 
1379082eeacSAndy Fleming 	return 0;
1389082eeacSAndy Fleming }
1399082eeacSAndy Fleming 
140*1b564cecSJiandong Zheng static int bcm_cygnus_startup(struct phy_device *phydev)
141*1b564cecSJiandong Zheng {
142*1b564cecSJiandong Zheng 	/* Read the Status (2x to make sure link is right) */
143*1b564cecSJiandong Zheng 	genphy_update_link(phydev);
144*1b564cecSJiandong Zheng 	genphy_parse_link(phydev);
145*1b564cecSJiandong Zheng 
146*1b564cecSJiandong Zheng 	return 0;
147*1b564cecSJiandong Zheng }
148*1b564cecSJiandong Zheng 
149*1b564cecSJiandong Zheng static int bcm_cygnus_config(struct phy_device *phydev)
150*1b564cecSJiandong Zheng {
151*1b564cecSJiandong Zheng 	genphy_config_aneg(phydev);
152*1b564cecSJiandong Zheng 
153*1b564cecSJiandong Zheng 	phy_reset(phydev);
154*1b564cecSJiandong Zheng 
155*1b564cecSJiandong Zheng 	return 0;
156*1b564cecSJiandong Zheng }
157*1b564cecSJiandong Zheng 
1589082eeacSAndy Fleming /*
1599082eeacSAndy Fleming  * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
1609082eeacSAndy Fleming  * 0x42 - "Operating Mode Status Register"
1619082eeacSAndy Fleming  */
1629082eeacSAndy Fleming static int bcm5482_is_serdes(struct phy_device *phydev)
1639082eeacSAndy Fleming {
1649082eeacSAndy Fleming 	u16 val;
1659082eeacSAndy Fleming 	int serdes = 0;
1669082eeacSAndy Fleming 
1679082eeacSAndy Fleming 	phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
1689082eeacSAndy Fleming 			MIIM_BCM54XX_EXP_SEL_ER | 0x42);
1699082eeacSAndy Fleming 	val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
1709082eeacSAndy Fleming 
1719082eeacSAndy Fleming 	switch (val & 0x1f) {
1729082eeacSAndy Fleming 	case 0x0d:	/* RGMII-to-100Base-FX */
1739082eeacSAndy Fleming 	case 0x0e:	/* RGMII-to-SGMII */
1749082eeacSAndy Fleming 	case 0x0f:	/* RGMII-to-SerDes */
1759082eeacSAndy Fleming 	case 0x12:	/* SGMII-to-SerDes */
1769082eeacSAndy Fleming 	case 0x13:	/* SGMII-to-100Base-FX */
1779082eeacSAndy Fleming 	case 0x16:	/* SerDes-to-Serdes */
1789082eeacSAndy Fleming 		serdes = 1;
1799082eeacSAndy Fleming 		break;
1809082eeacSAndy Fleming 	case 0x6:	/* RGMII-to-Copper */
1819082eeacSAndy Fleming 	case 0x14:	/* SGMII-to-Copper */
1829082eeacSAndy Fleming 	case 0x17:	/* SerDes-to-Copper */
1839082eeacSAndy Fleming 		break;
1849082eeacSAndy Fleming 	default:
1859082eeacSAndy Fleming 		printf("ERROR, invalid PHY mode (0x%x\n)", val);
1869082eeacSAndy Fleming 		break;
1879082eeacSAndy Fleming 	}
1889082eeacSAndy Fleming 
1899082eeacSAndy Fleming 	return serdes;
1909082eeacSAndy Fleming }
1919082eeacSAndy Fleming 
1929082eeacSAndy Fleming /*
1939082eeacSAndy Fleming  * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
1949082eeacSAndy Fleming  * Mode Status Register"
1959082eeacSAndy Fleming  */
1969082eeacSAndy Fleming static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
1979082eeacSAndy Fleming {
1989082eeacSAndy Fleming 	u16 val;
1999082eeacSAndy Fleming 	int i = 0;
2009082eeacSAndy Fleming 
2019082eeacSAndy Fleming 	/* Wait 1s for link - Clause 37 autonegotiation happens very fast */
2029082eeacSAndy Fleming 	while (1) {
2039082eeacSAndy Fleming 		phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
2049082eeacSAndy Fleming 				MIIM_BCM54XX_EXP_SEL_ER | 0x42);
2059082eeacSAndy Fleming 		val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
2069082eeacSAndy Fleming 
2079082eeacSAndy Fleming 		if (val & 0x8000)
2089082eeacSAndy Fleming 			break;
2099082eeacSAndy Fleming 
2109082eeacSAndy Fleming 		if (i++ > 1000) {
2119082eeacSAndy Fleming 			phydev->link = 0;
2129082eeacSAndy Fleming 			return 1;
2139082eeacSAndy Fleming 		}
2149082eeacSAndy Fleming 
2159082eeacSAndy Fleming 		udelay(1000);	/* 1 ms */
2169082eeacSAndy Fleming 	}
2179082eeacSAndy Fleming 
2189082eeacSAndy Fleming 	phydev->link = 1;
2199082eeacSAndy Fleming 	switch ((val >> 13) & 0x3) {
2209082eeacSAndy Fleming 	case (0x00):
2219082eeacSAndy Fleming 		phydev->speed = 10;
2229082eeacSAndy Fleming 		break;
2239082eeacSAndy Fleming 	case (0x01):
2249082eeacSAndy Fleming 		phydev->speed = 100;
2259082eeacSAndy Fleming 		break;
2269082eeacSAndy Fleming 	case (0x02):
2279082eeacSAndy Fleming 		phydev->speed = 1000;
2289082eeacSAndy Fleming 		break;
2299082eeacSAndy Fleming 	}
2309082eeacSAndy Fleming 
2319082eeacSAndy Fleming 	phydev->duplex = (val & 0x1000) == 0x1000;
2329082eeacSAndy Fleming 
2339082eeacSAndy Fleming 	return 0;
2349082eeacSAndy Fleming }
2359082eeacSAndy Fleming 
2369082eeacSAndy Fleming /*
2379082eeacSAndy Fleming  * Figure out if BCM5482 is in serdes or copper mode and determine link
2389082eeacSAndy Fleming  * configuration accordingly
2399082eeacSAndy Fleming  */
2409082eeacSAndy Fleming static int bcm5482_startup(struct phy_device *phydev)
2419082eeacSAndy Fleming {
2429082eeacSAndy Fleming 	if (bcm5482_is_serdes(phydev)) {
2439082eeacSAndy Fleming 		bcm5482_parse_serdes_sr(phydev);
2449082eeacSAndy Fleming 		phydev->port = PORT_FIBRE;
2459082eeacSAndy Fleming 	} else {
2469082eeacSAndy Fleming 		/* Wait for auto-negotiation to complete or fail */
2479082eeacSAndy Fleming 		genphy_update_link(phydev);
2489082eeacSAndy Fleming 		/* Parse BCM54xx copper aux status register */
2499082eeacSAndy Fleming 		bcm54xx_parse_status(phydev);
2509082eeacSAndy Fleming 	}
2519082eeacSAndy Fleming 
2529082eeacSAndy Fleming 	return 0;
2539082eeacSAndy Fleming }
2549082eeacSAndy Fleming 
2559082eeacSAndy Fleming static struct phy_driver BCM5461S_driver = {
2569082eeacSAndy Fleming 	.name = "Broadcom BCM5461S",
2579082eeacSAndy Fleming 	.uid = 0x2060c0,
2589082eeacSAndy Fleming 	.mask = 0xfffff0,
2599082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
2609082eeacSAndy Fleming 	.config = &bcm5461_config,
2619082eeacSAndy Fleming 	.startup = &bcm54xx_startup,
2629082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
2639082eeacSAndy Fleming };
2649082eeacSAndy Fleming 
2659082eeacSAndy Fleming static struct phy_driver BCM5464S_driver = {
2669082eeacSAndy Fleming 	.name = "Broadcom BCM5464S",
2679082eeacSAndy Fleming 	.uid = 0x2060b0,
2689082eeacSAndy Fleming 	.mask = 0xfffff0,
2699082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
2709082eeacSAndy Fleming 	.config = &bcm5461_config,
2719082eeacSAndy Fleming 	.startup = &bcm54xx_startup,
2729082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
2739082eeacSAndy Fleming };
2749082eeacSAndy Fleming 
2759082eeacSAndy Fleming static struct phy_driver BCM5482S_driver = {
2769082eeacSAndy Fleming 	.name = "Broadcom BCM5482S",
2779082eeacSAndy Fleming 	.uid = 0x143bcb0,
2789082eeacSAndy Fleming 	.mask = 0xffffff0,
2799082eeacSAndy Fleming 	.features = PHY_GBIT_FEATURES,
2809082eeacSAndy Fleming 	.config = &bcm5482_config,
2819082eeacSAndy Fleming 	.startup = &bcm5482_startup,
2829082eeacSAndy Fleming 	.shutdown = &genphy_shutdown,
2839082eeacSAndy Fleming };
2849082eeacSAndy Fleming 
285*1b564cecSJiandong Zheng static struct phy_driver BCM_CYGNUS_driver = {
286*1b564cecSJiandong Zheng 	.name = "Broadcom CYGNUS GPHY",
287*1b564cecSJiandong Zheng 	.uid = 0xae025200,
288*1b564cecSJiandong Zheng 	.mask = 0xfffff0,
289*1b564cecSJiandong Zheng 	.features = PHY_GBIT_FEATURES,
290*1b564cecSJiandong Zheng 	.config = &bcm_cygnus_config,
291*1b564cecSJiandong Zheng 	.startup = &bcm_cygnus_startup,
292*1b564cecSJiandong Zheng 	.shutdown = &genphy_shutdown,
293*1b564cecSJiandong Zheng };
294*1b564cecSJiandong Zheng 
2959082eeacSAndy Fleming int phy_broadcom_init(void)
2969082eeacSAndy Fleming {
2979082eeacSAndy Fleming 	phy_register(&BCM5482S_driver);
2989082eeacSAndy Fleming 	phy_register(&BCM5464S_driver);
2999082eeacSAndy Fleming 	phy_register(&BCM5461S_driver);
300*1b564cecSJiandong Zheng 	phy_register(&BCM_CYGNUS_driver);
3019082eeacSAndy Fleming 
3029082eeacSAndy Fleming 	return 0;
3039082eeacSAndy Fleming }
304