xref: /linux/drivers/net/phy/broadcom.c (revision 39bfb3c12d792181de0cf3306be1ea03664a1b05)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2c4b41c9fSMaciej W. Rozycki /*
3c4b41c9fSMaciej W. Rozycki  *	drivers/net/phy/broadcom.c
4c4b41c9fSMaciej W. Rozycki  *
5c4b41c9fSMaciej W. Rozycki  *	Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
6c4b41c9fSMaciej W. Rozycki  *	transceivers.
7c4b41c9fSMaciej W. Rozycki  *
8c4b41c9fSMaciej W. Rozycki  *	Copyright (c) 2006  Maciej W. Rozycki
9c4b41c9fSMaciej W. Rozycki  *
10c4b41c9fSMaciej W. Rozycki  *	Inspired by code written by Amy Fong.
11c4b41c9fSMaciej W. Rozycki  */
12c4b41c9fSMaciej W. Rozycki 
13a1cba561SArun Parameswaran #include "bcm-phy-lib.h"
14bf8bfc43SFlorian Fainelli #include <linux/delay.h>
15c4b41c9fSMaciej W. Rozycki #include <linux/module.h>
16c4b41c9fSMaciej W. Rozycki #include <linux/phy.h>
178649f13dSMatt Carlson #include <linux/brcmphy.h>
18b14995acSJon Mason #include <linux/of.h>
19d9221e66SMatt Carlson 
20d9221e66SMatt Carlson #define BRCM_PHY_MODEL(phydev) \
21d9221e66SMatt Carlson 	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
22d9221e66SMatt Carlson 
2332e5a8d6SMatt Carlson #define BRCM_PHY_REV(phydev) \
2432e5a8d6SMatt Carlson 	((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
2532e5a8d6SMatt Carlson 
26c4b41c9fSMaciej W. Rozycki MODULE_DESCRIPTION("Broadcom PHY driver");
27c4b41c9fSMaciej W. Rozycki MODULE_AUTHOR("Maciej W. Rozycki");
28c4b41c9fSMaciej W. Rozycki MODULE_LICENSE("GPL");
29c4b41c9fSMaciej W. Rozycki 
3015acf89eSJonathan Lemon struct bcm54xx_phy_priv {
3115acf89eSJonathan Lemon 	u64	*stats;
3215acf89eSJonathan Lemon 	struct bcm_ptp_private *ptp;
3315acf89eSJonathan Lemon };
3415acf89eSJonathan Lemon 
35042cb564STao Ren static int bcm54xx_config_clock_delay(struct phy_device *phydev)
36b14995acSJon Mason {
37b14995acSJon Mason 	int rc, val;
38b14995acSJon Mason 
3973333626SAbhishek Shah 	/* handling PHY's internal RX clock delay */
40b14995acSJon Mason 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
41b14995acSJon Mason 	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
4273333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
4373333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
4473333626SAbhishek Shah 		/* Disable RGMII RXC-RXD skew */
4573333626SAbhishek Shah 		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
4673333626SAbhishek Shah 	}
4773333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
4873333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
4973333626SAbhishek Shah 		/* Enable RGMII RXC-RXD skew */
5073333626SAbhishek Shah 		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
5173333626SAbhishek Shah 	}
52b14995acSJon Mason 	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
53b14995acSJon Mason 				  val);
54b14995acSJon Mason 	if (rc < 0)
55b14995acSJon Mason 		return rc;
56b14995acSJon Mason 
5773333626SAbhishek Shah 	/* handling PHY's internal TX clock delay */
58b14995acSJon Mason 	val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
5973333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
6073333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
6173333626SAbhishek Shah 		/* Disable internal TX clock delay */
62b14995acSJon Mason 		val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
6373333626SAbhishek Shah 	}
6473333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
6573333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
6673333626SAbhishek Shah 		/* Enable internal TX clock delay */
6773333626SAbhishek Shah 		val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
6873333626SAbhishek Shah 	}
69b14995acSJon Mason 	rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
70b14995acSJon Mason 	if (rc < 0)
71b14995acSJon Mason 		return rc;
72b14995acSJon Mason 
73b14995acSJon Mason 	return 0;
74b14995acSJon Mason }
75b14995acSJon Mason 
76133bf7b4SFlorian Fainelli static int bcm54210e_config_init(struct phy_device *phydev)
77133bf7b4SFlorian Fainelli {
78133bf7b4SFlorian Fainelli 	int val;
79133bf7b4SFlorian Fainelli 
80133bf7b4SFlorian Fainelli 	bcm54xx_config_clock_delay(phydev);
81133bf7b4SFlorian Fainelli 
82133bf7b4SFlorian Fainelli 	if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
83133bf7b4SFlorian Fainelli 		val = phy_read(phydev, MII_CTRL1000);
84133bf7b4SFlorian Fainelli 		val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
85133bf7b4SFlorian Fainelli 		phy_write(phydev, MII_CTRL1000, val);
86133bf7b4SFlorian Fainelli 	}
87133bf7b4SFlorian Fainelli 
88133bf7b4SFlorian Fainelli 	return 0;
89133bf7b4SFlorian Fainelli }
90133bf7b4SFlorian Fainelli 
91133bf7b4SFlorian Fainelli static int bcm54612e_config_init(struct phy_device *phydev)
92133bf7b4SFlorian Fainelli {
93133bf7b4SFlorian Fainelli 	int reg;
94133bf7b4SFlorian Fainelli 
95133bf7b4SFlorian Fainelli 	bcm54xx_config_clock_delay(phydev);
96133bf7b4SFlorian Fainelli 
97133bf7b4SFlorian Fainelli 	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
98133bf7b4SFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
99133bf7b4SFlorian Fainelli 		int err;
100133bf7b4SFlorian Fainelli 
101133bf7b4SFlorian Fainelli 		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
102133bf7b4SFlorian Fainelli 		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
103133bf7b4SFlorian Fainelli 					BCM54612E_LED4_CLK125OUT_EN | reg);
104133bf7b4SFlorian Fainelli 
105133bf7b4SFlorian Fainelli 		if (err < 0)
106133bf7b4SFlorian Fainelli 			return err;
107133bf7b4SFlorian Fainelli 	}
108133bf7b4SFlorian Fainelli 
109133bf7b4SFlorian Fainelli 	return 0;
110133bf7b4SFlorian Fainelli }
111133bf7b4SFlorian Fainelli 
1123afd0218SRobert Hancock static int bcm54616s_config_init(struct phy_device *phydev)
1133afd0218SRobert Hancock {
1143afd0218SRobert Hancock 	int rc, val;
1153afd0218SRobert Hancock 
1163afd0218SRobert Hancock 	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
1173afd0218SRobert Hancock 	    phydev->interface != PHY_INTERFACE_MODE_1000BASEX)
1183afd0218SRobert Hancock 		return 0;
1193afd0218SRobert Hancock 
1203afd0218SRobert Hancock 	/* Ensure proper interface mode is selected. */
1213afd0218SRobert Hancock 	/* Disable RGMII mode */
1223afd0218SRobert Hancock 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
1233afd0218SRobert Hancock 	if (val < 0)
1243afd0218SRobert Hancock 		return val;
1253afd0218SRobert Hancock 	val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN;
1263afd0218SRobert Hancock 	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
1273afd0218SRobert Hancock 	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
1283afd0218SRobert Hancock 				  val);
1293afd0218SRobert Hancock 	if (rc < 0)
1303afd0218SRobert Hancock 		return rc;
1313afd0218SRobert Hancock 
1323afd0218SRobert Hancock 	/* Select 1000BASE-X register set (primary SerDes) */
1333afd0218SRobert Hancock 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
1343afd0218SRobert Hancock 	if (val < 0)
1353afd0218SRobert Hancock 		return val;
1363afd0218SRobert Hancock 	val |= BCM54XX_SHD_MODE_1000BX;
1373afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
1383afd0218SRobert Hancock 	if (rc < 0)
1393afd0218SRobert Hancock 		return rc;
1403afd0218SRobert Hancock 
1413afd0218SRobert Hancock 	/* Power down SerDes interface */
1423afd0218SRobert Hancock 	rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
1433afd0218SRobert Hancock 	if (rc < 0)
1443afd0218SRobert Hancock 		return rc;
1453afd0218SRobert Hancock 
1463afd0218SRobert Hancock 	/* Select proper interface mode */
1473afd0218SRobert Hancock 	val &= ~BCM54XX_SHD_INTF_SEL_MASK;
1483afd0218SRobert Hancock 	val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ?
1493afd0218SRobert Hancock 		BCM54XX_SHD_INTF_SEL_SGMII :
1503afd0218SRobert Hancock 		BCM54XX_SHD_INTF_SEL_GBIC;
1513afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
1523afd0218SRobert Hancock 	if (rc < 0)
1533afd0218SRobert Hancock 		return rc;
1543afd0218SRobert Hancock 
1553afd0218SRobert Hancock 	/* Power up SerDes interface */
1563afd0218SRobert Hancock 	rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
1573afd0218SRobert Hancock 	if (rc < 0)
1583afd0218SRobert Hancock 		return rc;
1593afd0218SRobert Hancock 
1603afd0218SRobert Hancock 	/* Select copper register set */
1613afd0218SRobert Hancock 	val &= ~BCM54XX_SHD_MODE_1000BX;
1623afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
1633afd0218SRobert Hancock 	if (rc < 0)
1643afd0218SRobert Hancock 		return rc;
1653afd0218SRobert Hancock 
1663afd0218SRobert Hancock 	/* Power up copper interface */
1673afd0218SRobert Hancock 	return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
1683afd0218SRobert Hancock }
1693afd0218SRobert Hancock 
17047b1b53bSMatt Carlson /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
171772638b6SMatt Carlson static int bcm50610_a0_workaround(struct phy_device *phydev)
172772638b6SMatt Carlson {
173772638b6SMatt Carlson 	int err;
174772638b6SMatt Carlson 
175a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
17647b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
17747b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
17847b1b53bSMatt Carlson 	if (err < 0)
17947b1b53bSMatt Carlson 		return err;
18047b1b53bSMatt Carlson 
181a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
18247b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
18347b1b53bSMatt Carlson 	if (err < 0)
18447b1b53bSMatt Carlson 		return err;
18547b1b53bSMatt Carlson 
186a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
18747b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP75_VDACCTRL);
18847b1b53bSMatt Carlson 	if (err < 0)
18947b1b53bSMatt Carlson 		return err;
19047b1b53bSMatt Carlson 
191a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
19247b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP96_MYST);
19347b1b53bSMatt Carlson 	if (err < 0)
19447b1b53bSMatt Carlson 		return err;
19547b1b53bSMatt Carlson 
196a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
19747b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP97_MYST);
19847b1b53bSMatt Carlson 
19947b1b53bSMatt Carlson 	return err;
20047b1b53bSMatt Carlson }
20147b1b53bSMatt Carlson 
20247b1b53bSMatt Carlson static int bcm54xx_phydsp_config(struct phy_device *phydev)
20347b1b53bSMatt Carlson {
20447b1b53bSMatt Carlson 	int err, err2;
20547b1b53bSMatt Carlson 
20647b1b53bSMatt Carlson 	/* Enable the SMDSP clock */
207772638b6SMatt Carlson 	err = bcm54xx_auxctl_write(phydev,
208772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
209772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
210772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
211772638b6SMatt Carlson 	if (err < 0)
212772638b6SMatt Carlson 		return err;
213772638b6SMatt Carlson 
214219c6efeSMatt Carlson 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
215219c6efeSMatt Carlson 	    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
216219c6efeSMatt Carlson 		/* Clear bit 9 to fix a phy interop issue. */
217a1cba561SArun Parameswaran 		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
218219c6efeSMatt Carlson 					MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
219219c6efeSMatt Carlson 		if (err < 0)
220219c6efeSMatt Carlson 			goto error;
221219c6efeSMatt Carlson 
222219c6efeSMatt Carlson 		if (phydev->drv->phy_id == PHY_ID_BCM50610) {
22347b1b53bSMatt Carlson 			err = bcm50610_a0_workaround(phydev);
224219c6efeSMatt Carlson 			if (err < 0)
225219c6efeSMatt Carlson 				goto error;
226219c6efeSMatt Carlson 		}
227219c6efeSMatt Carlson 	}
22847b1b53bSMatt Carlson 
22947b1b53bSMatt Carlson 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
23047b1b53bSMatt Carlson 		int val;
23147b1b53bSMatt Carlson 
232a1cba561SArun Parameswaran 		val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
23347b1b53bSMatt Carlson 		if (val < 0)
234772638b6SMatt Carlson 			goto error;
235772638b6SMatt Carlson 
23647b1b53bSMatt Carlson 		val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
237a1cba561SArun Parameswaran 		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
23847b1b53bSMatt Carlson 	}
239772638b6SMatt Carlson 
240772638b6SMatt Carlson error:
24147b1b53bSMatt Carlson 	/* Disable the SMDSP clock */
24247b1b53bSMatt Carlson 	err2 = bcm54xx_auxctl_write(phydev,
243772638b6SMatt Carlson 				    MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
244772638b6SMatt Carlson 				    MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
245772638b6SMatt Carlson 
24647b1b53bSMatt Carlson 	/* Return the first error reported. */
24747b1b53bSMatt Carlson 	return err ? err : err2;
248772638b6SMatt Carlson }
249772638b6SMatt Carlson 
25032e5a8d6SMatt Carlson static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
25132e5a8d6SMatt Carlson {
2525ee6f6a1SRoel Kluin 	u32 orig;
2535ee6f6a1SRoel Kluin 	int val;
254c704dc23SMatt Carlson 	bool clk125en = true;
25532e5a8d6SMatt Carlson 
25632e5a8d6SMatt Carlson 	/* Abort if we are using an untested phy. */
2577ec4e7d3Sroel kluin 	if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
2587ec4e7d3Sroel kluin 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
2590ececcfcSFlorian Fainelli 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
2605d4358edSFlorian Fainelli 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E &&
261b0ed0bbfSKevin Lo 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
262b0ed0bbfSKevin Lo 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
26332e5a8d6SMatt Carlson 		return;
26432e5a8d6SMatt Carlson 
265a1cba561SArun Parameswaran 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
26632e5a8d6SMatt Carlson 	if (val < 0)
26732e5a8d6SMatt Carlson 		return;
26832e5a8d6SMatt Carlson 
26932e5a8d6SMatt Carlson 	orig = val;
27032e5a8d6SMatt Carlson 
27132e5a8d6SMatt Carlson 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
27232e5a8d6SMatt Carlson 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
27332e5a8d6SMatt Carlson 	    BRCM_PHY_REV(phydev) >= 0x3) {
274c704dc23SMatt Carlson 		/*
275c704dc23SMatt Carlson 		 * Here, bit 0 _disables_ CLK125 when set.
276c704dc23SMatt Carlson 		 * This bit is set by default.
277c704dc23SMatt Carlson 		 */
278c704dc23SMatt Carlson 		clk125en = false;
27932e5a8d6SMatt Carlson 	} else {
280c704dc23SMatt Carlson 		if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
281b0ed0bbfSKevin Lo 			if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
28232e5a8d6SMatt Carlson 				/* Here, bit 0 _enables_ CLK125 when set */
28332e5a8d6SMatt Carlson 				val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
284b0ed0bbfSKevin Lo 			}
285c704dc23SMatt Carlson 			clk125en = false;
28632e5a8d6SMatt Carlson 		}
28732e5a8d6SMatt Carlson 	}
28832e5a8d6SMatt Carlson 
28923677ce3SJoe Perches 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
290c704dc23SMatt Carlson 		val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
291c704dc23SMatt Carlson 	else
292c704dc23SMatt Carlson 		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
293c704dc23SMatt Carlson 
294b0ed0bbfSKevin Lo 	if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
2955d4358edSFlorian Fainelli 		if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E ||
2965d4358edSFlorian Fainelli 		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
297ad4e1e48SKevin Lo 		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
2985d4358edSFlorian Fainelli 			val |= BCM54XX_SHD_SCR3_RXCTXC_DIS;
299b0ed0bbfSKevin Lo 		else
30052fae083SMatt Carlson 			val |= BCM54XX_SHD_SCR3_TRDDAPD;
301b0ed0bbfSKevin Lo 	}
30252fae083SMatt Carlson 
30332e5a8d6SMatt Carlson 	if (orig != val)
304a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
305c704dc23SMatt Carlson 
306a1cba561SArun Parameswaran 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
307c704dc23SMatt Carlson 	if (val < 0)
308c704dc23SMatt Carlson 		return;
309c704dc23SMatt Carlson 
310c704dc23SMatt Carlson 	orig = val;
311c704dc23SMatt Carlson 
31223677ce3SJoe Perches 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
313c704dc23SMatt Carlson 		val |= BCM54XX_SHD_APD_EN;
314c704dc23SMatt Carlson 	else
315c704dc23SMatt Carlson 		val &= ~BCM54XX_SHD_APD_EN;
316c704dc23SMatt Carlson 
317c704dc23SMatt Carlson 	if (orig != val)
318a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
31932e5a8d6SMatt Carlson }
32032e5a8d6SMatt Carlson 
32115acf89eSJonathan Lemon static void bcm54xx_ptp_stop(struct phy_device *phydev)
32215acf89eSJonathan Lemon {
32315acf89eSJonathan Lemon 	struct bcm54xx_phy_priv *priv = phydev->priv;
32415acf89eSJonathan Lemon 
32515acf89eSJonathan Lemon 	if (priv->ptp)
32615acf89eSJonathan Lemon 		bcm_ptp_stop(priv->ptp);
32715acf89eSJonathan Lemon }
32815acf89eSJonathan Lemon 
32915acf89eSJonathan Lemon static void bcm54xx_ptp_config_init(struct phy_device *phydev)
33015acf89eSJonathan Lemon {
33115acf89eSJonathan Lemon 	struct bcm54xx_phy_priv *priv = phydev->priv;
33215acf89eSJonathan Lemon 
33315acf89eSJonathan Lemon 	if (priv->ptp)
33415acf89eSJonathan Lemon 		bcm_ptp_config_init(phydev);
33515acf89eSJonathan Lemon }
33615acf89eSJonathan Lemon 
337c4b41c9fSMaciej W. Rozycki static int bcm54xx_config_init(struct phy_device *phydev)
338c4b41c9fSMaciej W. Rozycki {
33973333626SAbhishek Shah 	int reg, err, val;
340c4b41c9fSMaciej W. Rozycki 
341c4b41c9fSMaciej W. Rozycki 	reg = phy_read(phydev, MII_BCM54XX_ECR);
342c4b41c9fSMaciej W. Rozycki 	if (reg < 0)
343c4b41c9fSMaciej W. Rozycki 		return reg;
344c4b41c9fSMaciej W. Rozycki 
345c4b41c9fSMaciej W. Rozycki 	/* Mask interrupts globally.  */
346c4b41c9fSMaciej W. Rozycki 	reg |= MII_BCM54XX_ECR_IM;
347c4b41c9fSMaciej W. Rozycki 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
348c4b41c9fSMaciej W. Rozycki 	if (err < 0)
349c4b41c9fSMaciej W. Rozycki 		return err;
350c4b41c9fSMaciej W. Rozycki 
351c4b41c9fSMaciej W. Rozycki 	/* Unmask events we are interested in.  */
352c4b41c9fSMaciej W. Rozycki 	reg = ~(MII_BCM54XX_INT_DUPLEX |
353c4b41c9fSMaciej W. Rozycki 		MII_BCM54XX_INT_SPEED |
354c4b41c9fSMaciej W. Rozycki 		MII_BCM54XX_INT_LINK);
355c4b41c9fSMaciej W. Rozycki 	err = phy_write(phydev, MII_BCM54XX_IMR, reg);
356c4b41c9fSMaciej W. Rozycki 	if (err < 0)
357c4b41c9fSMaciej W. Rozycki 		return err;
358772638b6SMatt Carlson 
35963a14ce4SMatt Carlson 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
36063a14ce4SMatt Carlson 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
36163a14ce4SMatt Carlson 	    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
362a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
36363a14ce4SMatt Carlson 
36432e5a8d6SMatt Carlson 	bcm54xx_adjust_rxrefclk(phydev);
36532e5a8d6SMatt Carlson 
3663afd0218SRobert Hancock 	switch (BRCM_PHY_MODEL(phydev)) {
367b1dd9bf6SFlorian Fainelli 	case PHY_ID_BCM50610:
368b1dd9bf6SFlorian Fainelli 	case PHY_ID_BCM50610M:
369b1dd9bf6SFlorian Fainelli 		err = bcm54xx_config_clock_delay(phydev);
370b1dd9bf6SFlorian Fainelli 		break;
3713afd0218SRobert Hancock 	case PHY_ID_BCM54210E:
3720fc9ae10SRafał Miłecki 		err = bcm54210e_config_init(phydev);
3733afd0218SRobert Hancock 		break;
3743afd0218SRobert Hancock 	case PHY_ID_BCM54612E:
37562e13097SRafał Miłecki 		err = bcm54612e_config_init(phydev);
3763afd0218SRobert Hancock 		break;
3773afd0218SRobert Hancock 	case PHY_ID_BCM54616S:
3783afd0218SRobert Hancock 		err = bcm54616s_config_init(phydev);
3793afd0218SRobert Hancock 		break;
3803afd0218SRobert Hancock 	case PHY_ID_BCM54810:
38173333626SAbhishek Shah 		/* For BCM54810, we need to disable BroadR-Reach function */
38273333626SAbhishek Shah 		val = bcm_phy_read_exp(phydev,
38373333626SAbhishek Shah 				       BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
38473333626SAbhishek Shah 		val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
38573333626SAbhishek Shah 		err = bcm_phy_write_exp(phydev,
38673333626SAbhishek Shah 					BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
38773333626SAbhishek Shah 					val);
3883afd0218SRobert Hancock 		break;
389b14995acSJon Mason 	}
3903afd0218SRobert Hancock 	if (err)
3913afd0218SRobert Hancock 		return err;
392b14995acSJon Mason 
39347b1b53bSMatt Carlson 	bcm54xx_phydsp_config(phydev);
394d9221e66SMatt Carlson 
395b5d007e2SRobert Hancock 	/* For non-SFP setups, encode link speed into LED1 and LED3 pair
396b5d007e2SRobert Hancock 	 * (green/amber).
397450895d0SVladimir Oltean 	 * Also flash these two LEDs on activity. This means configuring
398450895d0SVladimir Oltean 	 * them for MULTICOLOR and encoding link/activity into them.
399b5d007e2SRobert Hancock 	 * Don't do this for devices on an SFP module, since some of these
400b5d007e2SRobert Hancock 	 * use the LED outputs to control the SFP LOS signal, and changing
401b5d007e2SRobert Hancock 	 * these settings will cause LOS to malfunction.
402450895d0SVladimir Oltean 	 */
403b5d007e2SRobert Hancock 	if (!phy_on_sfp(phydev)) {
404450895d0SVladimir Oltean 		val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
405450895d0SVladimir Oltean 			BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
406450895d0SVladimir Oltean 		bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val);
407450895d0SVladimir Oltean 
408450895d0SVladimir Oltean 		val = BCM_LED_MULTICOLOR_IN_PHASE |
409450895d0SVladimir Oltean 			BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
410450895d0SVladimir Oltean 			BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
411450895d0SVladimir Oltean 		bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
412b5d007e2SRobert Hancock 	}
413450895d0SVladimir Oltean 
41415acf89eSJonathan Lemon 	bcm54xx_ptp_config_init(phydev);
41515acf89eSJonathan Lemon 
416c4b41c9fSMaciej W. Rozycki 	return 0;
417c4b41c9fSMaciej W. Rozycki }
418c4b41c9fSMaciej W. Rozycki 
419d6da08edSFlorian Fainelli static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
420d6da08edSFlorian Fainelli {
421d6da08edSFlorian Fainelli 	int ret = 0;
422d6da08edSFlorian Fainelli 
423d6da08edSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND))
424d6da08edSFlorian Fainelli 		return ret;
425d6da08edSFlorian Fainelli 
426d6da08edSFlorian Fainelli 	ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL);
427d6da08edSFlorian Fainelli 	if (ret < 0)
428d6da08edSFlorian Fainelli 		goto out;
429d6da08edSFlorian Fainelli 
430d6da08edSFlorian Fainelli 	if (enable)
431d6da08edSFlorian Fainelli 		ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP;
432d6da08edSFlorian Fainelli 	else
433d6da08edSFlorian Fainelli 		ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP);
434d6da08edSFlorian Fainelli 
435d6da08edSFlorian Fainelli 	ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, ret);
436d6da08edSFlorian Fainelli out:
437d6da08edSFlorian Fainelli 	return ret;
438d6da08edSFlorian Fainelli }
439d6da08edSFlorian Fainelli 
440d6da08edSFlorian Fainelli static int bcm54xx_suspend(struct phy_device *phydev)
441d6da08edSFlorian Fainelli {
442d6da08edSFlorian Fainelli 	int ret;
443d6da08edSFlorian Fainelli 
44415acf89eSJonathan Lemon 	bcm54xx_ptp_stop(phydev);
44515acf89eSJonathan Lemon 
446d6da08edSFlorian Fainelli 	/* We cannot use a read/modify/write here otherwise the PHY gets into
447d6da08edSFlorian Fainelli 	 * a bad state where its LEDs keep flashing, thus defeating the purpose
448d6da08edSFlorian Fainelli 	 * of low power mode.
449d6da08edSFlorian Fainelli 	 */
450d6da08edSFlorian Fainelli 	ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
451d6da08edSFlorian Fainelli 	if (ret < 0)
452d6da08edSFlorian Fainelli 		return ret;
453d6da08edSFlorian Fainelli 
454d6da08edSFlorian Fainelli 	return bcm54xx_iddq_set(phydev, true);
455d6da08edSFlorian Fainelli }
456d6da08edSFlorian Fainelli 
457fe26821fSFlorian Fainelli static int bcm54xx_resume(struct phy_device *phydev)
458fe26821fSFlorian Fainelli {
459fe26821fSFlorian Fainelli 	int ret;
460fe26821fSFlorian Fainelli 
461d6da08edSFlorian Fainelli 	ret = bcm54xx_iddq_set(phydev, false);
462d6da08edSFlorian Fainelli 	if (ret < 0)
463d6da08edSFlorian Fainelli 		return ret;
464d6da08edSFlorian Fainelli 
465fe26821fSFlorian Fainelli 	/* Writes to register other than BMCR would be ignored
466fe26821fSFlorian Fainelli 	 * unless we clear the PDOWN bit first
467fe26821fSFlorian Fainelli 	 */
468fe26821fSFlorian Fainelli 	ret = genphy_resume(phydev);
469fe26821fSFlorian Fainelli 	if (ret < 0)
470fe26821fSFlorian Fainelli 		return ret;
471fe26821fSFlorian Fainelli 
4727a1468baSFlorian Fainelli 	/* Upon exiting power down, the PHY remains in an internal reset state
4737a1468baSFlorian Fainelli 	 * for 40us
4747a1468baSFlorian Fainelli 	 */
4757a1468baSFlorian Fainelli 	fsleep(40);
4767a1468baSFlorian Fainelli 
477d6da08edSFlorian Fainelli 	/* Issue a soft reset after clearing the power down bit
478d6da08edSFlorian Fainelli 	 * and before doing any other configuration.
479d6da08edSFlorian Fainelli 	 */
480d6da08edSFlorian Fainelli 	if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) {
481d6da08edSFlorian Fainelli 		ret = genphy_soft_reset(phydev);
482d6da08edSFlorian Fainelli 		if (ret < 0)
483d6da08edSFlorian Fainelli 			return ret;
484d6da08edSFlorian Fainelli 	}
485d6da08edSFlorian Fainelli 
486fe26821fSFlorian Fainelli 	return bcm54xx_config_init(phydev);
487fe26821fSFlorian Fainelli }
488fe26821fSFlorian Fainelli 
489b0ed0bbfSKevin Lo static int bcm54811_config_init(struct phy_device *phydev)
490b0ed0bbfSKevin Lo {
491b0ed0bbfSKevin Lo 	int err, reg;
492b0ed0bbfSKevin Lo 
493b0ed0bbfSKevin Lo 	/* Disable BroadR-Reach function. */
494b0ed0bbfSKevin Lo 	reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
495b0ed0bbfSKevin Lo 	reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
496b0ed0bbfSKevin Lo 	err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
497b0ed0bbfSKevin Lo 				reg);
498b0ed0bbfSKevin Lo 	if (err < 0)
499b0ed0bbfSKevin Lo 		return err;
500b0ed0bbfSKevin Lo 
501b0ed0bbfSKevin Lo 	err = bcm54xx_config_init(phydev);
502b0ed0bbfSKevin Lo 
503b0ed0bbfSKevin Lo 	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
504b0ed0bbfSKevin Lo 	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
505b0ed0bbfSKevin Lo 		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
506b0ed0bbfSKevin Lo 		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
507b0ed0bbfSKevin Lo 					BCM54612E_LED4_CLK125OUT_EN | reg);
508b0ed0bbfSKevin Lo 		if (err < 0)
509b0ed0bbfSKevin Lo 			return err;
510b0ed0bbfSKevin Lo 	}
511b0ed0bbfSKevin Lo 
512b0ed0bbfSKevin Lo 	return err;
513b0ed0bbfSKevin Lo }
514b0ed0bbfSKevin Lo 
51557bb7e22SAnton Vorontsov static int bcm5481_config_aneg(struct phy_device *phydev)
51657bb7e22SAnton Vorontsov {
517b14995acSJon Mason 	struct device_node *np = phydev->mdio.dev.of_node;
51857bb7e22SAnton Vorontsov 	int ret;
51957bb7e22SAnton Vorontsov 
52029f20dd6SJonathan Neuschäfer 	/* Aneg firstly. */
52157bb7e22SAnton Vorontsov 	ret = genphy_config_aneg(phydev);
52257bb7e22SAnton Vorontsov 
52357bb7e22SAnton Vorontsov 	/* Then we can set up the delay. */
524042cb564STao Ren 	bcm54xx_config_clock_delay(phydev);
52557bb7e22SAnton Vorontsov 
526b14995acSJon Mason 	if (of_property_read_bool(np, "enet-phy-lane-swap")) {
527b14995acSJon Mason 		/* Lane Swap - Undocumented register...magic! */
528b14995acSJon Mason 		ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
529b14995acSJon Mason 					0x11B);
530b14995acSJon Mason 		if (ret < 0)
531b14995acSJon Mason 			return ret;
532b14995acSJon Mason 	}
533b14995acSJon Mason 
53457bb7e22SAnton Vorontsov 	return ret;
53557bb7e22SAnton Vorontsov }
53657bb7e22SAnton Vorontsov 
53717d3a83aSFlorian Fainelli struct bcm54616s_phy_priv {
53817d3a83aSFlorian Fainelli 	bool mode_1000bx_en;
53917d3a83aSFlorian Fainelli };
54017d3a83aSFlorian Fainelli 
541b9bcb953STao Ren static int bcm54616s_probe(struct phy_device *phydev)
542b9bcb953STao Ren {
54317d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv;
5443afd0218SRobert Hancock 	int val;
545b9bcb953STao Ren 
54617d3a83aSFlorian Fainelli 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
54717d3a83aSFlorian Fainelli 	if (!priv)
54817d3a83aSFlorian Fainelli 		return -ENOMEM;
54917d3a83aSFlorian Fainelli 
55017d3a83aSFlorian Fainelli 	phydev->priv = priv;
55117d3a83aSFlorian Fainelli 
552b9bcb953STao Ren 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
553b9bcb953STao Ren 	if (val < 0)
554b9bcb953STao Ren 		return val;
555b9bcb953STao Ren 
556b9bcb953STao Ren 	/* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0]
557b9bcb953STao Ren 	 * is 01b, and the link between PHY and its link partner can be
558b9bcb953STao Ren 	 * either 1000Base-X or 100Base-FX.
559b9bcb953STao Ren 	 * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
560b9bcb953STao Ren 	 * support is still missing as of now.
561b9bcb953STao Ren 	 */
5623afd0218SRobert Hancock 	if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
563b9bcb953STao Ren 		val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL);
564b9bcb953STao Ren 		if (val < 0)
565b9bcb953STao Ren 			return val;
566b9bcb953STao Ren 
567b9bcb953STao Ren 		/* Bit 0 of the SerDes 100-FX Control register, when set
568b9bcb953STao Ren 		 * to 1, sets the MII/RGMII -> 100BASE-FX configuration.
569b9bcb953STao Ren 		 * When this bit is set to 0, it sets the GMII/RGMII ->
570b9bcb953STao Ren 		 * 1000BASE-X configuration.
571b9bcb953STao Ren 		 */
572b9bcb953STao Ren 		if (!(val & BCM54616S_100FX_MODE))
57317d3a83aSFlorian Fainelli 			priv->mode_1000bx_en = true;
5744217a64eSMichael Walle 
5754217a64eSMichael Walle 		phydev->port = PORT_FIBRE;
576b9bcb953STao Ren 	}
577b9bcb953STao Ren 
578b9bcb953STao Ren 	return 0;
579b9bcb953STao Ren }
580b9bcb953STao Ren 
581042cb564STao Ren static int bcm54616s_config_aneg(struct phy_device *phydev)
582042cb564STao Ren {
58317d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv = phydev->priv;
584042cb564STao Ren 	int ret;
585042cb564STao Ren 
58629f20dd6SJonathan Neuschäfer 	/* Aneg firstly. */
58717d3a83aSFlorian Fainelli 	if (priv->mode_1000bx_en)
588b9bcb953STao Ren 		ret = genphy_c37_config_aneg(phydev);
589b9bcb953STao Ren 	else
590042cb564STao Ren 		ret = genphy_config_aneg(phydev);
591042cb564STao Ren 
592042cb564STao Ren 	/* Then we can set up the delay. */
593042cb564STao Ren 	bcm54xx_config_clock_delay(phydev);
594042cb564STao Ren 
595042cb564STao Ren 	return ret;
596042cb564STao Ren }
597042cb564STao Ren 
598b9bcb953STao Ren static int bcm54616s_read_status(struct phy_device *phydev)
599b9bcb953STao Ren {
60017d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv = phydev->priv;
601b9bcb953STao Ren 	int err;
602b9bcb953STao Ren 
60317d3a83aSFlorian Fainelli 	if (priv->mode_1000bx_en)
604b9bcb953STao Ren 		err = genphy_c37_read_status(phydev);
605b9bcb953STao Ren 	else
606b9bcb953STao Ren 		err = genphy_read_status(phydev);
607b9bcb953STao Ren 
608b9bcb953STao Ren 	return err;
609b9bcb953STao Ren }
610b9bcb953STao Ren 
611d7a2ed92SMatt Carlson static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
612d7a2ed92SMatt Carlson {
613d7a2ed92SMatt Carlson 	int val;
614d7a2ed92SMatt Carlson 
615d7a2ed92SMatt Carlson 	val = phy_read(phydev, reg);
616d7a2ed92SMatt Carlson 	if (val < 0)
617d7a2ed92SMatt Carlson 		return val;
618d7a2ed92SMatt Carlson 
619d7a2ed92SMatt Carlson 	return phy_write(phydev, reg, val | set);
620d7a2ed92SMatt Carlson }
621d7a2ed92SMatt Carlson 
622d7a2ed92SMatt Carlson static int brcm_fet_config_init(struct phy_device *phydev)
623d7a2ed92SMatt Carlson {
624d7a2ed92SMatt Carlson 	int reg, err, err2, brcmtest;
625d7a2ed92SMatt Carlson 
626d7a2ed92SMatt Carlson 	/* Reset the PHY to bring it to a known state. */
627d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
628d7a2ed92SMatt Carlson 	if (err < 0)
629d7a2ed92SMatt Carlson 		return err;
630d7a2ed92SMatt Carlson 
631bf8bfc43SFlorian Fainelli 	/* The datasheet indicates the PHY needs up to 1us to complete a reset,
632bf8bfc43SFlorian Fainelli 	 * build some slack here.
633bf8bfc43SFlorian Fainelli 	 */
634bf8bfc43SFlorian Fainelli 	usleep_range(1000, 2000);
635bf8bfc43SFlorian Fainelli 
636bf8bfc43SFlorian Fainelli 	/* The PHY requires 65 MDC clock cycles to complete a write operation
637bf8bfc43SFlorian Fainelli 	 * and turnaround the line properly.
638bf8bfc43SFlorian Fainelli 	 *
639bf8bfc43SFlorian Fainelli 	 * We ignore -EIO here as the MDIO controller (e.g.: mdio-bcm-unimac)
640bf8bfc43SFlorian Fainelli 	 * may flag the lack of turn-around as a read failure. This is
641bf8bfc43SFlorian Fainelli 	 * particularly true with this combination since the MDIO controller
642bf8bfc43SFlorian Fainelli 	 * only used 64 MDC cycles. This is not a critical failure in this
643bf8bfc43SFlorian Fainelli 	 * specific case and it has no functional impact otherwise, so we let
644bf8bfc43SFlorian Fainelli 	 * that one go through. If there is a genuine bus error, the next read
645bf8bfc43SFlorian Fainelli 	 * of MII_BRCM_FET_INTREG will error out.
646bf8bfc43SFlorian Fainelli 	 */
647bf8bfc43SFlorian Fainelli 	err = phy_read(phydev, MII_BMCR);
648bf8bfc43SFlorian Fainelli 	if (err < 0 && err != -EIO)
649bf8bfc43SFlorian Fainelli 		return err;
650bf8bfc43SFlorian Fainelli 
651d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
652d7a2ed92SMatt Carlson 	if (reg < 0)
653d7a2ed92SMatt Carlson 		return reg;
654d7a2ed92SMatt Carlson 
655d7a2ed92SMatt Carlson 	/* Unmask events we are interested in and mask interrupts globally. */
656d7a2ed92SMatt Carlson 	reg = MII_BRCM_FET_IR_DUPLEX_EN |
657d7a2ed92SMatt Carlson 	      MII_BRCM_FET_IR_SPEED_EN |
658d7a2ed92SMatt Carlson 	      MII_BRCM_FET_IR_LINK_EN |
659d7a2ed92SMatt Carlson 	      MII_BRCM_FET_IR_ENABLE |
660d7a2ed92SMatt Carlson 	      MII_BRCM_FET_IR_MASK;
661d7a2ed92SMatt Carlson 
662d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
663d7a2ed92SMatt Carlson 	if (err < 0)
664d7a2ed92SMatt Carlson 		return err;
665d7a2ed92SMatt Carlson 
666d7a2ed92SMatt Carlson 	/* Enable shadow register access */
667d7a2ed92SMatt Carlson 	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
668d7a2ed92SMatt Carlson 	if (brcmtest < 0)
669d7a2ed92SMatt Carlson 		return brcmtest;
670d7a2ed92SMatt Carlson 
671d7a2ed92SMatt Carlson 	reg = brcmtest | MII_BRCM_FET_BT_SRE;
672d7a2ed92SMatt Carlson 
673d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
674d7a2ed92SMatt Carlson 	if (err < 0)
675d7a2ed92SMatt Carlson 		return err;
676d7a2ed92SMatt Carlson 
677d7a2ed92SMatt Carlson 	/* Set the LED mode */
678d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
679d7a2ed92SMatt Carlson 	if (reg < 0) {
680d7a2ed92SMatt Carlson 		err = reg;
681d7a2ed92SMatt Carlson 		goto done;
682d7a2ed92SMatt Carlson 	}
683d7a2ed92SMatt Carlson 
684d7a2ed92SMatt Carlson 	reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
685d7a2ed92SMatt Carlson 	reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
686d7a2ed92SMatt Carlson 
687d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
688d7a2ed92SMatt Carlson 	if (err < 0)
689d7a2ed92SMatt Carlson 		goto done;
690d7a2ed92SMatt Carlson 
691d7a2ed92SMatt Carlson 	/* Enable auto MDIX */
692d7a2ed92SMatt Carlson 	err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
693d7a2ed92SMatt Carlson 				       MII_BRCM_FET_SHDW_MC_FAME);
694d7a2ed92SMatt Carlson 	if (err < 0)
695d7a2ed92SMatt Carlson 		goto done;
696d7a2ed92SMatt Carlson 
697cdd4e09dSMatt Carlson 	if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
698d7a2ed92SMatt Carlson 		/* Enable auto power down */
699d7a2ed92SMatt Carlson 		err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
700d7a2ed92SMatt Carlson 					       MII_BRCM_FET_SHDW_AS2_APDE);
701cdd4e09dSMatt Carlson 	}
702d7a2ed92SMatt Carlson 
703d7a2ed92SMatt Carlson done:
704d7a2ed92SMatt Carlson 	/* Disable shadow register access */
705d7a2ed92SMatt Carlson 	err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
706d7a2ed92SMatt Carlson 	if (!err)
707d7a2ed92SMatt Carlson 		err = err2;
708d7a2ed92SMatt Carlson 
709d7a2ed92SMatt Carlson 	return err;
710d7a2ed92SMatt Carlson }
711d7a2ed92SMatt Carlson 
712d7a2ed92SMatt Carlson static int brcm_fet_ack_interrupt(struct phy_device *phydev)
713d7a2ed92SMatt Carlson {
714d7a2ed92SMatt Carlson 	int reg;
715d7a2ed92SMatt Carlson 
716d7a2ed92SMatt Carlson 	/* Clear pending interrupts.  */
717d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
718d7a2ed92SMatt Carlson 	if (reg < 0)
719d7a2ed92SMatt Carlson 		return reg;
720d7a2ed92SMatt Carlson 
721d7a2ed92SMatt Carlson 	return 0;
722d7a2ed92SMatt Carlson }
723d7a2ed92SMatt Carlson 
724d7a2ed92SMatt Carlson static int brcm_fet_config_intr(struct phy_device *phydev)
725d7a2ed92SMatt Carlson {
726d7a2ed92SMatt Carlson 	int reg, err;
727d7a2ed92SMatt Carlson 
728d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
729d7a2ed92SMatt Carlson 	if (reg < 0)
730d7a2ed92SMatt Carlson 		return reg;
731d7a2ed92SMatt Carlson 
73215772e4dSIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
73315772e4dSIoana Ciornei 		err = brcm_fet_ack_interrupt(phydev);
73415772e4dSIoana Ciornei 		if (err)
73515772e4dSIoana Ciornei 			return err;
736d7a2ed92SMatt Carlson 
73715772e4dSIoana Ciornei 		reg &= ~MII_BRCM_FET_IR_MASK;
738d7a2ed92SMatt Carlson 		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
73915772e4dSIoana Ciornei 	} else {
74015772e4dSIoana Ciornei 		reg |= MII_BRCM_FET_IR_MASK;
74115772e4dSIoana Ciornei 		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
74215772e4dSIoana Ciornei 		if (err)
74315772e4dSIoana Ciornei 			return err;
74415772e4dSIoana Ciornei 
74515772e4dSIoana Ciornei 		err = brcm_fet_ack_interrupt(phydev);
74615772e4dSIoana Ciornei 	}
74715772e4dSIoana Ciornei 
748d7a2ed92SMatt Carlson 	return err;
749d7a2ed92SMatt Carlson }
750d7a2ed92SMatt Carlson 
7514567d5c3SIoana Ciornei static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev)
7524567d5c3SIoana Ciornei {
7534567d5c3SIoana Ciornei 	int irq_status;
7544567d5c3SIoana Ciornei 
7554567d5c3SIoana Ciornei 	irq_status = phy_read(phydev, MII_BRCM_FET_INTREG);
7564567d5c3SIoana Ciornei 	if (irq_status < 0) {
7574567d5c3SIoana Ciornei 		phy_error(phydev);
7584567d5c3SIoana Ciornei 		return IRQ_NONE;
7594567d5c3SIoana Ciornei 	}
7604567d5c3SIoana Ciornei 
7614567d5c3SIoana Ciornei 	if (irq_status == 0)
7624567d5c3SIoana Ciornei 		return IRQ_NONE;
7634567d5c3SIoana Ciornei 
7644567d5c3SIoana Ciornei 	phy_trigger_machine(phydev);
7654567d5c3SIoana Ciornei 
7664567d5c3SIoana Ciornei 	return IRQ_HANDLED;
7674567d5c3SIoana Ciornei }
7684567d5c3SIoana Ciornei 
7695a32fcdbSFlorian Fainelli static int bcm54xx_phy_probe(struct phy_device *phydev)
77028dc4c8fSFlorian Fainelli {
7715a32fcdbSFlorian Fainelli 	struct bcm54xx_phy_priv *priv;
77228dc4c8fSFlorian Fainelli 
77328dc4c8fSFlorian Fainelli 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
77428dc4c8fSFlorian Fainelli 	if (!priv)
77528dc4c8fSFlorian Fainelli 		return -ENOMEM;
77628dc4c8fSFlorian Fainelli 
77728dc4c8fSFlorian Fainelli 	phydev->priv = priv;
77828dc4c8fSFlorian Fainelli 
77928dc4c8fSFlorian Fainelli 	priv->stats = devm_kcalloc(&phydev->mdio.dev,
78028dc4c8fSFlorian Fainelli 				   bcm_phy_get_sset_count(phydev), sizeof(u64),
78128dc4c8fSFlorian Fainelli 				   GFP_KERNEL);
78228dc4c8fSFlorian Fainelli 	if (!priv->stats)
78328dc4c8fSFlorian Fainelli 		return -ENOMEM;
78428dc4c8fSFlorian Fainelli 
78515acf89eSJonathan Lemon 	priv->ptp = bcm_ptp_probe(phydev);
78615acf89eSJonathan Lemon 	if (IS_ERR(priv->ptp))
78715acf89eSJonathan Lemon 		return PTR_ERR(priv->ptp);
78815acf89eSJonathan Lemon 
78928dc4c8fSFlorian Fainelli 	return 0;
79028dc4c8fSFlorian Fainelli }
79128dc4c8fSFlorian Fainelli 
7925a32fcdbSFlorian Fainelli static void bcm54xx_get_stats(struct phy_device *phydev,
79328dc4c8fSFlorian Fainelli 			      struct ethtool_stats *stats, u64 *data)
79428dc4c8fSFlorian Fainelli {
7955a32fcdbSFlorian Fainelli 	struct bcm54xx_phy_priv *priv = phydev->priv;
79628dc4c8fSFlorian Fainelli 
79728dc4c8fSFlorian Fainelli 	bcm_phy_get_stats(phydev, priv->stats, stats, data);
79828dc4c8fSFlorian Fainelli }
79928dc4c8fSFlorian Fainelli 
8008dc84dcdSFlorian Fainelli static void bcm54xx_link_change_notify(struct phy_device *phydev)
8018dc84dcdSFlorian Fainelli {
8028dc84dcdSFlorian Fainelli 	u16 mask = MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE |
8038dc84dcdSFlorian Fainelli 		   MII_BCM54XX_EXP_EXP08_FORCE_DAC_WAKE;
8048dc84dcdSFlorian Fainelli 	int ret;
8058dc84dcdSFlorian Fainelli 
8068dc84dcdSFlorian Fainelli 	if (phydev->state != PHY_RUNNING)
8078dc84dcdSFlorian Fainelli 		return;
8088dc84dcdSFlorian Fainelli 
8098dc84dcdSFlorian Fainelli 	/* Don't change the DAC wake settings if auto power down
8108dc84dcdSFlorian Fainelli 	 * is not requested.
8118dc84dcdSFlorian Fainelli 	 */
8128dc84dcdSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
8138dc84dcdSFlorian Fainelli 		return;
8148dc84dcdSFlorian Fainelli 
8158dc84dcdSFlorian Fainelli 	ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08);
8168dc84dcdSFlorian Fainelli 	if (ret < 0)
8178dc84dcdSFlorian Fainelli 		return;
8188dc84dcdSFlorian Fainelli 
8198dc84dcdSFlorian Fainelli 	/* Enable/disable 10BaseT auto and forced early DAC wake depending
8208dc84dcdSFlorian Fainelli 	 * on the negotiated speed, those settings should only be done
8218dc84dcdSFlorian Fainelli 	 * for 10Mbits/sec.
8228dc84dcdSFlorian Fainelli 	 */
8238dc84dcdSFlorian Fainelli 	if (phydev->speed == SPEED_10)
8248dc84dcdSFlorian Fainelli 		ret |= mask;
8258dc84dcdSFlorian Fainelli 	else
8268dc84dcdSFlorian Fainelli 		ret &= ~mask;
8278dc84dcdSFlorian Fainelli 	bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
8288dc84dcdSFlorian Fainelli }
8298dc84dcdSFlorian Fainelli 
830d5bf9071SChristian Hohnstaedt static struct phy_driver broadcom_drivers[] = {
831d5bf9071SChristian Hohnstaedt {
832fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5411,
833c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
834c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5411",
835dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
8365a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
8375a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
8385a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
8395a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
840c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
841a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
8424567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
8438dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
844d5bf9071SChristian Hohnstaedt }, {
845fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5421,
846c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
847c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5421",
848dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
8495a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
8505a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
8515a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
8525a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
853c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
854a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
8554567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
8568dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
857d5bf9071SChristian Hohnstaedt }, {
8580fc9ae10SRafał Miłecki 	.phy_id		= PHY_ID_BCM54210E,
8590fc9ae10SRafał Miłecki 	.phy_id_mask	= 0xfffffff0,
8600fc9ae10SRafał Miłecki 	.name		= "Broadcom BCM54210E",
861dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
8625a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
8635a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
8645a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
8655a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
8660fc9ae10SRafał Miłecki 	.config_init	= bcm54xx_config_init,
8670fc9ae10SRafał Miłecki 	.config_intr	= bcm_phy_config_intr,
8684567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
8698dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
870d6da08edSFlorian Fainelli 	.suspend	= bcm54xx_suspend,
871d6da08edSFlorian Fainelli 	.resume		= bcm54xx_resume,
8720fc9ae10SRafał Miłecki }, {
873fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5461,
874c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
875c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5461",
876dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
8775a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
8785a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
8795a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
8805a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
881c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
882a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
8834567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
8848dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
885d5bf9071SChristian Hohnstaedt }, {
886d92ead16SXo Wang 	.phy_id		= PHY_ID_BCM54612E,
887d92ead16SXo Wang 	.phy_id_mask	= 0xfffffff0,
888d92ead16SXo Wang 	.name		= "Broadcom BCM54612E",
889dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
8905a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
8915a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
8925a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
8935a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
894d92ead16SXo Wang 	.config_init	= bcm54xx_config_init,
895d92ead16SXo Wang 	.config_intr	= bcm_phy_config_intr,
8964567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
8978dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
898d92ead16SXo Wang }, {
8993bca4cf6SAlessio Igor Bogani 	.phy_id		= PHY_ID_BCM54616S,
9003bca4cf6SAlessio Igor Bogani 	.phy_id_mask	= 0xfffffff0,
9013bca4cf6SAlessio Igor Bogani 	.name		= "Broadcom BCM54616S",
902dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
903d15c7e87SRobert Hancock 	.soft_reset     = genphy_soft_reset,
9043bca4cf6SAlessio Igor Bogani 	.config_init	= bcm54xx_config_init,
905042cb564STao Ren 	.config_aneg	= bcm54616s_config_aneg,
906a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
9074567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
908b9bcb953STao Ren 	.read_status	= bcm54616s_read_status,
909b9bcb953STao Ren 	.probe		= bcm54616s_probe,
9108dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
9113bca4cf6SAlessio Igor Bogani }, {
912fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5464,
913b1394f96SPaul Gortmaker 	.phy_id_mask	= 0xfffffff0,
914b1394f96SPaul Gortmaker 	.name		= "Broadcom BCM5464",
915dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
9165a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9175a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9185a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9195a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
920b1394f96SPaul Gortmaker 	.config_init	= bcm54xx_config_init,
921a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
9224567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
923283da99aSVladimir Oltean 	.suspend	= genphy_suspend,
924283da99aSVladimir Oltean 	.resume		= genphy_resume,
9258dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
926d5bf9071SChristian Hohnstaedt }, {
927fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5481,
92857bb7e22SAnton Vorontsov 	.phy_id_mask	= 0xfffffff0,
92957bb7e22SAnton Vorontsov 	.name		= "Broadcom BCM5481",
930dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
9315a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9325a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9335a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9345a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
93557bb7e22SAnton Vorontsov 	.config_init	= bcm54xx_config_init,
9369753c21fSHeiner Kallweit 	.config_aneg	= bcm5481_config_aneg,
937a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
9384567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
9398dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
940d5bf9071SChristian Hohnstaedt }, {
941b14995acSJon Mason 	.phy_id         = PHY_ID_BCM54810,
942b14995acSJon Mason 	.phy_id_mask    = 0xfffffff0,
943b14995acSJon Mason 	.name           = "Broadcom BCM54810",
944dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
9455a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9465a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9475a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9485a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
949b14995acSJon Mason 	.config_init    = bcm54xx_config_init,
9509753c21fSHeiner Kallweit 	.config_aneg    = bcm5481_config_aneg,
951b14995acSJon Mason 	.config_intr    = bcm_phy_config_intr,
9524567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
95372e78d22SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
954fe26821fSFlorian Fainelli 	.resume		= bcm54xx_resume,
9558dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
956b14995acSJon Mason }, {
957b0ed0bbfSKevin Lo 	.phy_id         = PHY_ID_BCM54811,
958b0ed0bbfSKevin Lo 	.phy_id_mask    = 0xfffffff0,
959b0ed0bbfSKevin Lo 	.name           = "Broadcom BCM54811",
960b0ed0bbfSKevin Lo 	/* PHY_GBIT_FEATURES */
9615a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9625a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9635a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9645a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
965b0ed0bbfSKevin Lo 	.config_init    = bcm54811_config_init,
966b0ed0bbfSKevin Lo 	.config_aneg    = bcm5481_config_aneg,
967b0ed0bbfSKevin Lo 	.config_intr    = bcm_phy_config_intr,
9684567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
96972e78d22SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
970b0ed0bbfSKevin Lo 	.resume		= bcm54xx_resume,
9718dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
972b0ed0bbfSKevin Lo }, {
973fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5482,
97403157ac3SNate Case 	.phy_id_mask	= 0xfffffff0,
97503157ac3SNate Case 	.name		= "Broadcom BCM5482",
976dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
9775a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9785a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9795a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9805a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
9811e2e61afSMichael Walle 	.config_init	= bcm54xx_config_init,
982a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
9834567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
9848dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
985d5bf9071SChristian Hohnstaedt }, {
986772638b6SMatt Carlson 	.phy_id		= PHY_ID_BCM50610,
987772638b6SMatt Carlson 	.phy_id_mask	= 0xfffffff0,
988772638b6SMatt Carlson 	.name		= "Broadcom BCM50610",
989dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
9905a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
9915a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
9925a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
9935a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
994772638b6SMatt Carlson 	.config_init	= bcm54xx_config_init,
995a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
9964567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
9978dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
99838b6a907SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
99938b6a907SFlorian Fainelli 	.resume		= bcm54xx_resume,
1000d5bf9071SChristian Hohnstaedt }, {
10014f4598fdSMatt Carlson 	.phy_id		= PHY_ID_BCM50610M,
10024f4598fdSMatt Carlson 	.phy_id_mask	= 0xfffffff0,
10034f4598fdSMatt Carlson 	.name		= "Broadcom BCM50610M",
1004dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
10055a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
10065a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
10075a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
10085a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
10094f4598fdSMatt Carlson 	.config_init	= bcm54xx_config_init,
1010a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
10114567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
10128dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
101338b6a907SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
101438b6a907SFlorian Fainelli 	.resume		= bcm54xx_resume,
1015d5bf9071SChristian Hohnstaedt }, {
1016d9221e66SMatt Carlson 	.phy_id		= PHY_ID_BCM57780,
10172fbb69aaSMatt Carlson 	.phy_id_mask	= 0xfffffff0,
10182fbb69aaSMatt Carlson 	.name		= "Broadcom BCM57780",
1019dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
10205a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
10215a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
10225a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
10235a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
10242fbb69aaSMatt Carlson 	.config_init	= bcm54xx_config_init,
1025a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
10264567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
10278dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1028d5bf9071SChristian Hohnstaedt }, {
10296a443a0fSMatt Carlson 	.phy_id		= PHY_ID_BCMAC131,
1030d7a2ed92SMatt Carlson 	.phy_id_mask	= 0xfffffff0,
1031d7a2ed92SMatt Carlson 	.name		= "Broadcom BCMAC131",
1032dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1033d7a2ed92SMatt Carlson 	.config_init	= brcm_fet_config_init,
1034d7a2ed92SMatt Carlson 	.config_intr	= brcm_fet_config_intr,
10354567d5c3SIoana Ciornei 	.handle_interrupt = brcm_fet_handle_interrupt,
1036d5bf9071SChristian Hohnstaedt }, {
10377a938f80SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5241,
10387a938f80SDmitry Baryshkov 	.phy_id_mask	= 0xfffffff0,
10397a938f80SDmitry Baryshkov 	.name		= "Broadcom BCM5241",
1040dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
10417a938f80SDmitry Baryshkov 	.config_init	= brcm_fet_config_init,
10427a938f80SDmitry Baryshkov 	.config_intr	= brcm_fet_config_intr,
10434567d5c3SIoana Ciornei 	.handle_interrupt = brcm_fet_handle_interrupt,
104428dc4c8fSFlorian Fainelli }, {
104528dc4c8fSFlorian Fainelli 	.phy_id		= PHY_ID_BCM5395,
104628dc4c8fSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
104728dc4c8fSFlorian Fainelli 	.name		= "Broadcom BCM5395",
104828dc4c8fSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
1049dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
105028dc4c8fSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
105128dc4c8fSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
10525a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
10535a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
10548dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
105523b83922SBhadram Varka }, {
1056123aff2aSFlorian Fainelli 	.phy_id		= PHY_ID_BCM53125,
1057123aff2aSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
1058123aff2aSFlorian Fainelli 	.name		= "Broadcom BCM53125",
1059123aff2aSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
1060123aff2aSFlorian Fainelli 	/* PHY_GBIT_FEATURES */
1061123aff2aSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
1062123aff2aSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
10635a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
10645a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1065123aff2aSFlorian Fainelli 	.config_init	= bcm54xx_config_init,
1066123aff2aSFlorian Fainelli 	.config_intr	= bcm_phy_config_intr,
10674567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
10688dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1069123aff2aSFlorian Fainelli }, {
1070*39bfb3c1SKurt Kanzenbach 	.phy_id		= PHY_ID_BCM53128,
1071*39bfb3c1SKurt Kanzenbach 	.phy_id_mask	= 0xfffffff0,
1072*39bfb3c1SKurt Kanzenbach 	.name		= "Broadcom BCM53128",
1073*39bfb3c1SKurt Kanzenbach 	.flags		= PHY_IS_INTERNAL,
1074*39bfb3c1SKurt Kanzenbach 	/* PHY_GBIT_FEATURES */
1075*39bfb3c1SKurt Kanzenbach 	.get_sset_count	= bcm_phy_get_sset_count,
1076*39bfb3c1SKurt Kanzenbach 	.get_strings	= bcm_phy_get_strings,
1077*39bfb3c1SKurt Kanzenbach 	.get_stats	= bcm54xx_get_stats,
1078*39bfb3c1SKurt Kanzenbach 	.probe		= bcm54xx_phy_probe,
1079*39bfb3c1SKurt Kanzenbach 	.config_init	= bcm54xx_config_init,
1080*39bfb3c1SKurt Kanzenbach 	.config_intr	= bcm_phy_config_intr,
1081*39bfb3c1SKurt Kanzenbach 	.handle_interrupt = bcm_phy_handle_interrupt,
1082*39bfb3c1SKurt Kanzenbach 	.link_change_notify	= bcm54xx_link_change_notify,
1083*39bfb3c1SKurt Kanzenbach }, {
108423b83922SBhadram Varka 	.phy_id         = PHY_ID_BCM89610,
108523b83922SBhadram Varka 	.phy_id_mask    = 0xfffffff0,
108623b83922SBhadram Varka 	.name           = "Broadcom BCM89610",
1087dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
10885a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
10895a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
10905a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
10915a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
109223b83922SBhadram Varka 	.config_init    = bcm54xx_config_init,
109323b83922SBhadram Varka 	.config_intr    = bcm_phy_config_intr,
10944567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
10958dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1096d5bf9071SChristian Hohnstaedt } };
10977a938f80SDmitry Baryshkov 
109850fd7150SJohan Hovold module_phy_driver(broadcom_drivers);
10994e4f10f6SDavid Woodhouse 
1100cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
1101fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5411, 0xfffffff0 },
1102fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5421, 0xfffffff0 },
11030fc9ae10SRafał Miłecki 	{ PHY_ID_BCM54210E, 0xfffffff0 },
1104fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5461, 0xfffffff0 },
1105d92ead16SXo Wang 	{ PHY_ID_BCM54612E, 0xfffffff0 },
11063bca4cf6SAlessio Igor Bogani 	{ PHY_ID_BCM54616S, 0xfffffff0 },
1107fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5464, 0xfffffff0 },
11083c25a860SAaro Koskinen 	{ PHY_ID_BCM5481, 0xfffffff0 },
1109b14995acSJon Mason 	{ PHY_ID_BCM54810, 0xfffffff0 },
1110b0ed0bbfSKevin Lo 	{ PHY_ID_BCM54811, 0xfffffff0 },
1111fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5482, 0xfffffff0 },
11124e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM50610, 0xfffffff0 },
11134e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM50610M, 0xfffffff0 },
11144e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM57780, 0xfffffff0 },
11154e4f10f6SDavid Woodhouse 	{ PHY_ID_BCMAC131, 0xfffffff0 },
11167a938f80SDmitry Baryshkov 	{ PHY_ID_BCM5241, 0xfffffff0 },
111728dc4c8fSFlorian Fainelli 	{ PHY_ID_BCM5395, 0xfffffff0 },
1118123aff2aSFlorian Fainelli 	{ PHY_ID_BCM53125, 0xfffffff0 },
1119*39bfb3c1SKurt Kanzenbach 	{ PHY_ID_BCM53128, 0xfffffff0 },
112023b83922SBhadram Varka 	{ PHY_ID_BCM89610, 0xfffffff0 },
11214e4f10f6SDavid Woodhouse 	{ }
11224e4f10f6SDavid Woodhouse };
11234e4f10f6SDavid Woodhouse 
11244e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
1125