xref: /linux/drivers/net/phy/broadcom.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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  *
8*03ab6c24SKamil Horák (2N)  *	Broadcom BCM54810, BCM54811 BroadR-Reach transceivers.
9*03ab6c24SKamil Horák (2N)  *
10c4b41c9fSMaciej W. Rozycki  *	Copyright (c) 2006  Maciej W. Rozycki
11c4b41c9fSMaciej W. Rozycki  *
12c4b41c9fSMaciej W. Rozycki  *	Inspired by code written by Amy Fong.
13c4b41c9fSMaciej W. Rozycki  */
14c4b41c9fSMaciej W. Rozycki 
15a1cba561SArun Parameswaran #include "bcm-phy-lib.h"
16bf8bfc43SFlorian Fainelli #include <linux/delay.h>
17c4b41c9fSMaciej W. Rozycki #include <linux/module.h>
18c4b41c9fSMaciej W. Rozycki #include <linux/phy.h>
198baddaa9SFlorian Fainelli #include <linux/pm_wakeup.h>
208649f13dSMatt Carlson #include <linux/brcmphy.h>
21b14995acSJon Mason #include <linux/of.h>
228baddaa9SFlorian Fainelli #include <linux/interrupt.h>
238baddaa9SFlorian Fainelli #include <linux/irq.h>
248baddaa9SFlorian Fainelli #include <linux/gpio/consumer.h>
25d9221e66SMatt Carlson 
26d9221e66SMatt Carlson #define BRCM_PHY_MODEL(phydev) \
27d9221e66SMatt Carlson 	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
28d9221e66SMatt Carlson 
2932e5a8d6SMatt Carlson #define BRCM_PHY_REV(phydev) \
3032e5a8d6SMatt Carlson 	((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
3132e5a8d6SMatt Carlson 
32c4b41c9fSMaciej W. Rozycki MODULE_DESCRIPTION("Broadcom PHY driver");
33c4b41c9fSMaciej W. Rozycki MODULE_AUTHOR("Maciej W. Rozycki");
34c4b41c9fSMaciej W. Rozycki MODULE_LICENSE("GPL");
35c4b41c9fSMaciej W. Rozycki 
3615acf89eSJonathan Lemon struct bcm54xx_phy_priv {
3715acf89eSJonathan Lemon 	u64	*stats;
3815acf89eSJonathan Lemon 	struct bcm_ptp_private *ptp;
398baddaa9SFlorian Fainelli 	int	wake_irq;
408baddaa9SFlorian Fainelli 	bool	wake_irq_enabled;
41*03ab6c24SKamil Horák (2N) 	bool	brr_mode;
42*03ab6c24SKamil Horák (2N) };
43*03ab6c24SKamil Horák (2N) 
44*03ab6c24SKamil Horák (2N) /* Link modes for BCM58411 PHY */
45*03ab6c24SKamil Horák (2N) static const int bcm54811_linkmodes[] = {
46*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
47*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
48*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
49*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
50*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
51*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
52*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_100baseT_Half_BIT,
53*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_10baseT_Full_BIT,
54*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_10baseT_Half_BIT
55*03ab6c24SKamil Horák (2N) };
56*03ab6c24SKamil Horák (2N) 
57*03ab6c24SKamil Horák (2N) /* Long-Distance Signaling (BroadR-Reach mode aneg) relevant linkmode bits */
58*03ab6c24SKamil Horák (2N) static const int lds_br_bits[] = {
59*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_Autoneg_BIT,
60*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_Pause_BIT,
61*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_Asym_Pause_BIT,
62*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
63*03ab6c24SKamil Horák (2N) 	ETHTOOL_LINK_MODE_100baseT1_Full_BIT
6415acf89eSJonathan Lemon };
6515acf89eSJonathan Lemon 
bcm54xx_phy_can_wakeup(struct phy_device * phydev)668baddaa9SFlorian Fainelli static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev)
678baddaa9SFlorian Fainelli {
688baddaa9SFlorian Fainelli 	struct bcm54xx_phy_priv *priv = phydev->priv;
698baddaa9SFlorian Fainelli 
708baddaa9SFlorian Fainelli 	return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0;
718baddaa9SFlorian Fainelli }
728baddaa9SFlorian Fainelli 
bcm54xx_config_clock_delay(struct phy_device * phydev)73042cb564STao Ren static int bcm54xx_config_clock_delay(struct phy_device *phydev)
74b14995acSJon Mason {
75b14995acSJon Mason 	int rc, val;
76b14995acSJon Mason 
7773333626SAbhishek Shah 	/* handling PHY's internal RX clock delay */
78b14995acSJon Mason 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
79b14995acSJon Mason 	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
8073333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
8173333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
8273333626SAbhishek Shah 		/* Disable RGMII RXC-RXD skew */
8373333626SAbhishek Shah 		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
8473333626SAbhishek Shah 	}
8573333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
8673333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
8773333626SAbhishek Shah 		/* Enable RGMII RXC-RXD skew */
8873333626SAbhishek Shah 		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
8973333626SAbhishek Shah 	}
90b14995acSJon Mason 	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
91b14995acSJon Mason 				  val);
92b14995acSJon Mason 	if (rc < 0)
93b14995acSJon Mason 		return rc;
94b14995acSJon Mason 
9573333626SAbhishek Shah 	/* handling PHY's internal TX clock delay */
96b14995acSJon Mason 	val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
9773333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
9873333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
9973333626SAbhishek Shah 		/* Disable internal TX clock delay */
100b14995acSJon Mason 		val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
10173333626SAbhishek Shah 	}
10273333626SAbhishek Shah 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
10373333626SAbhishek Shah 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
10473333626SAbhishek Shah 		/* Enable internal TX clock delay */
10573333626SAbhishek Shah 		val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
10673333626SAbhishek Shah 	}
107b14995acSJon Mason 	rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
108b14995acSJon Mason 	if (rc < 0)
109b14995acSJon Mason 		return rc;
110b14995acSJon Mason 
111b14995acSJon Mason 	return 0;
112b14995acSJon Mason }
113b14995acSJon Mason 
bcm54210e_config_init(struct phy_device * phydev)114133bf7b4SFlorian Fainelli static int bcm54210e_config_init(struct phy_device *phydev)
115133bf7b4SFlorian Fainelli {
116133bf7b4SFlorian Fainelli 	int val;
117133bf7b4SFlorian Fainelli 
118133bf7b4SFlorian Fainelli 	bcm54xx_config_clock_delay(phydev);
119133bf7b4SFlorian Fainelli 
120133bf7b4SFlorian Fainelli 	if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
121133bf7b4SFlorian Fainelli 		val = phy_read(phydev, MII_CTRL1000);
122133bf7b4SFlorian Fainelli 		val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
123133bf7b4SFlorian Fainelli 		phy_write(phydev, MII_CTRL1000, val);
124133bf7b4SFlorian Fainelli 	}
125133bf7b4SFlorian Fainelli 
126133bf7b4SFlorian Fainelli 	return 0;
127133bf7b4SFlorian Fainelli }
128133bf7b4SFlorian Fainelli 
bcm54612e_config_init(struct phy_device * phydev)129133bf7b4SFlorian Fainelli static int bcm54612e_config_init(struct phy_device *phydev)
130133bf7b4SFlorian Fainelli {
131133bf7b4SFlorian Fainelli 	int reg;
132133bf7b4SFlorian Fainelli 
133133bf7b4SFlorian Fainelli 	bcm54xx_config_clock_delay(phydev);
134133bf7b4SFlorian Fainelli 
135133bf7b4SFlorian Fainelli 	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
136133bf7b4SFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
137133bf7b4SFlorian Fainelli 		int err;
138133bf7b4SFlorian Fainelli 
139133bf7b4SFlorian Fainelli 		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
140133bf7b4SFlorian Fainelli 		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
141133bf7b4SFlorian Fainelli 					BCM54612E_LED4_CLK125OUT_EN | reg);
142133bf7b4SFlorian Fainelli 
143133bf7b4SFlorian Fainelli 		if (err < 0)
144133bf7b4SFlorian Fainelli 			return err;
145133bf7b4SFlorian Fainelli 	}
146133bf7b4SFlorian Fainelli 
147133bf7b4SFlorian Fainelli 	return 0;
148133bf7b4SFlorian Fainelli }
149133bf7b4SFlorian Fainelli 
bcm54616s_config_init(struct phy_device * phydev)1503afd0218SRobert Hancock static int bcm54616s_config_init(struct phy_device *phydev)
1513afd0218SRobert Hancock {
1523afd0218SRobert Hancock 	int rc, val;
1533afd0218SRobert Hancock 
1543afd0218SRobert Hancock 	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
1553afd0218SRobert Hancock 	    phydev->interface != PHY_INTERFACE_MODE_1000BASEX)
1563afd0218SRobert Hancock 		return 0;
1573afd0218SRobert Hancock 
1583afd0218SRobert Hancock 	/* Ensure proper interface mode is selected. */
1593afd0218SRobert Hancock 	/* Disable RGMII mode */
1603afd0218SRobert Hancock 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
1613afd0218SRobert Hancock 	if (val < 0)
1623afd0218SRobert Hancock 		return val;
1633afd0218SRobert Hancock 	val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN;
1643afd0218SRobert Hancock 	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
1653afd0218SRobert Hancock 	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
1663afd0218SRobert Hancock 				  val);
1673afd0218SRobert Hancock 	if (rc < 0)
1683afd0218SRobert Hancock 		return rc;
1693afd0218SRobert Hancock 
1703afd0218SRobert Hancock 	/* Select 1000BASE-X register set (primary SerDes) */
1713afd0218SRobert Hancock 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
1723afd0218SRobert Hancock 	if (val < 0)
1733afd0218SRobert Hancock 		return val;
1743afd0218SRobert Hancock 	val |= BCM54XX_SHD_MODE_1000BX;
1753afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
1763afd0218SRobert Hancock 	if (rc < 0)
1773afd0218SRobert Hancock 		return rc;
1783afd0218SRobert Hancock 
1793afd0218SRobert Hancock 	/* Power down SerDes interface */
1803afd0218SRobert Hancock 	rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
1813afd0218SRobert Hancock 	if (rc < 0)
1823afd0218SRobert Hancock 		return rc;
1833afd0218SRobert Hancock 
1843afd0218SRobert Hancock 	/* Select proper interface mode */
1853afd0218SRobert Hancock 	val &= ~BCM54XX_SHD_INTF_SEL_MASK;
1863afd0218SRobert Hancock 	val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ?
1873afd0218SRobert Hancock 		BCM54XX_SHD_INTF_SEL_SGMII :
1883afd0218SRobert Hancock 		BCM54XX_SHD_INTF_SEL_GBIC;
1893afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
1903afd0218SRobert Hancock 	if (rc < 0)
1913afd0218SRobert Hancock 		return rc;
1923afd0218SRobert Hancock 
1933afd0218SRobert Hancock 	/* Power up SerDes interface */
1943afd0218SRobert Hancock 	rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
1953afd0218SRobert Hancock 	if (rc < 0)
1963afd0218SRobert Hancock 		return rc;
1973afd0218SRobert Hancock 
1983afd0218SRobert Hancock 	/* Select copper register set */
1993afd0218SRobert Hancock 	val &= ~BCM54XX_SHD_MODE_1000BX;
2003afd0218SRobert Hancock 	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
2013afd0218SRobert Hancock 	if (rc < 0)
2023afd0218SRobert Hancock 		return rc;
2033afd0218SRobert Hancock 
2043afd0218SRobert Hancock 	/* Power up copper interface */
2053afd0218SRobert Hancock 	return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
2063afd0218SRobert Hancock }
2073afd0218SRobert Hancock 
20847b1b53bSMatt Carlson /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
bcm50610_a0_workaround(struct phy_device * phydev)209772638b6SMatt Carlson static int bcm50610_a0_workaround(struct phy_device *phydev)
210772638b6SMatt Carlson {
211772638b6SMatt Carlson 	int err;
212772638b6SMatt Carlson 
213a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
21447b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
21547b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
21647b1b53bSMatt Carlson 	if (err < 0)
21747b1b53bSMatt Carlson 		return err;
21847b1b53bSMatt Carlson 
219a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
22047b1b53bSMatt Carlson 				MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
22147b1b53bSMatt Carlson 	if (err < 0)
22247b1b53bSMatt Carlson 		return err;
22347b1b53bSMatt Carlson 
224a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
22547b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP75_VDACCTRL);
22647b1b53bSMatt Carlson 	if (err < 0)
22747b1b53bSMatt Carlson 		return err;
22847b1b53bSMatt Carlson 
229a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
23047b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP96_MYST);
23147b1b53bSMatt Carlson 	if (err < 0)
23247b1b53bSMatt Carlson 		return err;
23347b1b53bSMatt Carlson 
234a1cba561SArun Parameswaran 	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
23547b1b53bSMatt Carlson 				MII_BCM54XX_EXP_EXP97_MYST);
23647b1b53bSMatt Carlson 
23747b1b53bSMatt Carlson 	return err;
23847b1b53bSMatt Carlson }
23947b1b53bSMatt Carlson 
bcm54xx_phydsp_config(struct phy_device * phydev)24047b1b53bSMatt Carlson static int bcm54xx_phydsp_config(struct phy_device *phydev)
24147b1b53bSMatt Carlson {
24247b1b53bSMatt Carlson 	int err, err2;
24347b1b53bSMatt Carlson 
24447b1b53bSMatt Carlson 	/* Enable the SMDSP clock */
245772638b6SMatt Carlson 	err = bcm54xx_auxctl_write(phydev,
246772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
247772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
248772638b6SMatt Carlson 				   MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
249772638b6SMatt Carlson 	if (err < 0)
250772638b6SMatt Carlson 		return err;
251772638b6SMatt Carlson 
252219c6efeSMatt Carlson 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
253219c6efeSMatt Carlson 	    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
254219c6efeSMatt Carlson 		/* Clear bit 9 to fix a phy interop issue. */
255a1cba561SArun Parameswaran 		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
256219c6efeSMatt Carlson 					MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
257219c6efeSMatt Carlson 		if (err < 0)
258219c6efeSMatt Carlson 			goto error;
259219c6efeSMatt Carlson 
260219c6efeSMatt Carlson 		if (phydev->drv->phy_id == PHY_ID_BCM50610) {
26147b1b53bSMatt Carlson 			err = bcm50610_a0_workaround(phydev);
262219c6efeSMatt Carlson 			if (err < 0)
263219c6efeSMatt Carlson 				goto error;
264219c6efeSMatt Carlson 		}
265219c6efeSMatt Carlson 	}
26647b1b53bSMatt Carlson 
26747b1b53bSMatt Carlson 	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
26847b1b53bSMatt Carlson 		int val;
26947b1b53bSMatt Carlson 
270a1cba561SArun Parameswaran 		val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
27147b1b53bSMatt Carlson 		if (val < 0)
272772638b6SMatt Carlson 			goto error;
273772638b6SMatt Carlson 
27447b1b53bSMatt Carlson 		val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
275a1cba561SArun Parameswaran 		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
27647b1b53bSMatt Carlson 	}
277772638b6SMatt Carlson 
278772638b6SMatt Carlson error:
27947b1b53bSMatt Carlson 	/* Disable the SMDSP clock */
28047b1b53bSMatt Carlson 	err2 = bcm54xx_auxctl_write(phydev,
281772638b6SMatt Carlson 				    MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
282772638b6SMatt Carlson 				    MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
283772638b6SMatt Carlson 
28447b1b53bSMatt Carlson 	/* Return the first error reported. */
28547b1b53bSMatt Carlson 	return err ? err : err2;
286772638b6SMatt Carlson }
287772638b6SMatt Carlson 
bcm54xx_adjust_rxrefclk(struct phy_device * phydev)28832e5a8d6SMatt Carlson static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
28932e5a8d6SMatt Carlson {
2905ee6f6a1SRoel Kluin 	u32 orig;
2915ee6f6a1SRoel Kluin 	int val;
292c704dc23SMatt Carlson 	bool clk125en = true;
29332e5a8d6SMatt Carlson 
29432e5a8d6SMatt Carlson 	/* Abort if we are using an untested phy. */
2957ec4e7d3Sroel kluin 	if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
2967ec4e7d3Sroel kluin 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
2970ececcfcSFlorian Fainelli 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
2985d4358edSFlorian Fainelli 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E &&
299b0ed0bbfSKevin Lo 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
300b0ed0bbfSKevin Lo 	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
30132e5a8d6SMatt Carlson 		return;
30232e5a8d6SMatt Carlson 
303a1cba561SArun Parameswaran 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
30432e5a8d6SMatt Carlson 	if (val < 0)
30532e5a8d6SMatt Carlson 		return;
30632e5a8d6SMatt Carlson 
30732e5a8d6SMatt Carlson 	orig = val;
30832e5a8d6SMatt Carlson 
30932e5a8d6SMatt Carlson 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
31032e5a8d6SMatt Carlson 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
31132e5a8d6SMatt Carlson 	    BRCM_PHY_REV(phydev) >= 0x3) {
312c704dc23SMatt Carlson 		/*
313c704dc23SMatt Carlson 		 * Here, bit 0 _disables_ CLK125 when set.
314c704dc23SMatt Carlson 		 * This bit is set by default.
315c704dc23SMatt Carlson 		 */
316c704dc23SMatt Carlson 		clk125en = false;
31732e5a8d6SMatt Carlson 	} else {
318c704dc23SMatt Carlson 		if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
319b0ed0bbfSKevin Lo 			if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
32032e5a8d6SMatt Carlson 				/* Here, bit 0 _enables_ CLK125 when set */
32132e5a8d6SMatt Carlson 				val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
322b0ed0bbfSKevin Lo 			}
323c704dc23SMatt Carlson 			clk125en = false;
32432e5a8d6SMatt Carlson 		}
32532e5a8d6SMatt Carlson 	}
32632e5a8d6SMatt Carlson 
32723677ce3SJoe Perches 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
328c704dc23SMatt Carlson 		val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
329c704dc23SMatt Carlson 	else
330c704dc23SMatt Carlson 		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
331c704dc23SMatt Carlson 
332b0ed0bbfSKevin Lo 	if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
3335d4358edSFlorian Fainelli 		if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E ||
3345d4358edSFlorian Fainelli 		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
335ad4e1e48SKevin Lo 		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
3365d4358edSFlorian Fainelli 			val |= BCM54XX_SHD_SCR3_RXCTXC_DIS;
337b0ed0bbfSKevin Lo 		else
33852fae083SMatt Carlson 			val |= BCM54XX_SHD_SCR3_TRDDAPD;
339b0ed0bbfSKevin Lo 	}
34052fae083SMatt Carlson 
34132e5a8d6SMatt Carlson 	if (orig != val)
342a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
343c704dc23SMatt Carlson 
344a1cba561SArun Parameswaran 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
345c704dc23SMatt Carlson 	if (val < 0)
346c704dc23SMatt Carlson 		return;
347c704dc23SMatt Carlson 
348c704dc23SMatt Carlson 	orig = val;
349c704dc23SMatt Carlson 
35023677ce3SJoe Perches 	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
351c704dc23SMatt Carlson 		val |= BCM54XX_SHD_APD_EN;
352c704dc23SMatt Carlson 	else
353c704dc23SMatt Carlson 		val &= ~BCM54XX_SHD_APD_EN;
354c704dc23SMatt Carlson 
355c704dc23SMatt Carlson 	if (orig != val)
356a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
35732e5a8d6SMatt Carlson }
35832e5a8d6SMatt Carlson 
bcm54xx_ptp_stop(struct phy_device * phydev)35915acf89eSJonathan Lemon static void bcm54xx_ptp_stop(struct phy_device *phydev)
36015acf89eSJonathan Lemon {
36115acf89eSJonathan Lemon 	struct bcm54xx_phy_priv *priv = phydev->priv;
36215acf89eSJonathan Lemon 
36315acf89eSJonathan Lemon 	if (priv->ptp)
36415acf89eSJonathan Lemon 		bcm_ptp_stop(priv->ptp);
36515acf89eSJonathan Lemon }
36615acf89eSJonathan Lemon 
bcm54xx_ptp_config_init(struct phy_device * phydev)36715acf89eSJonathan Lemon static void bcm54xx_ptp_config_init(struct phy_device *phydev)
36815acf89eSJonathan Lemon {
36915acf89eSJonathan Lemon 	struct bcm54xx_phy_priv *priv = phydev->priv;
37015acf89eSJonathan Lemon 
37115acf89eSJonathan Lemon 	if (priv->ptp)
37215acf89eSJonathan Lemon 		bcm_ptp_config_init(phydev);
37315acf89eSJonathan Lemon }
37415acf89eSJonathan Lemon 
bcm5481x_set_brrmode(struct phy_device * phydev,bool on)375*03ab6c24SKamil Horák (2N) static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on)
376*03ab6c24SKamil Horák (2N) {
377*03ab6c24SKamil Horák (2N) 	int reg;
378*03ab6c24SKamil Horák (2N) 	int err;
379*03ab6c24SKamil Horák (2N) 	u16 val;
380*03ab6c24SKamil Horák (2N) 
381*03ab6c24SKamil Horák (2N) 	reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
382*03ab6c24SKamil Horák (2N) 
383*03ab6c24SKamil Horák (2N) 	if (reg < 0)
384*03ab6c24SKamil Horák (2N) 		return reg;
385*03ab6c24SKamil Horák (2N) 
386*03ab6c24SKamil Horák (2N) 	if (on)
387*03ab6c24SKamil Horák (2N) 		reg |= BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
388*03ab6c24SKamil Horák (2N) 	else
389*03ab6c24SKamil Horák (2N) 		reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
390*03ab6c24SKamil Horák (2N) 
391*03ab6c24SKamil Horák (2N) 	err = bcm_phy_write_exp(phydev,
392*03ab6c24SKamil Horák (2N) 				BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg);
393*03ab6c24SKamil Horák (2N) 	if (err)
394*03ab6c24SKamil Horák (2N) 		return err;
395*03ab6c24SKamil Horák (2N) 
396*03ab6c24SKamil Horák (2N) 	/* Ensure LRE or IEEE register set is accessed according to the brr
397*03ab6c24SKamil Horák (2N) 	 *  on/off, thus set the override
398*03ab6c24SKamil Horák (2N) 	 */
399*03ab6c24SKamil Horák (2N) 	val = BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN;
400*03ab6c24SKamil Horák (2N) 	if (!on)
401*03ab6c24SKamil Horák (2N) 		val |= BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL;
402*03ab6c24SKamil Horák (2N) 
403*03ab6c24SKamil Horák (2N) 	return bcm_phy_write_exp(phydev,
404*03ab6c24SKamil Horák (2N) 				 BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL, val);
405*03ab6c24SKamil Horák (2N) }
406*03ab6c24SKamil Horák (2N) 
bcm54811_config_init(struct phy_device * phydev)407*03ab6c24SKamil Horák (2N) static int bcm54811_config_init(struct phy_device *phydev)
408*03ab6c24SKamil Horák (2N) {
409*03ab6c24SKamil Horák (2N) 	struct bcm54xx_phy_priv *priv = phydev->priv;
410*03ab6c24SKamil Horák (2N) 	int err, reg;
411*03ab6c24SKamil Horák (2N) 
412*03ab6c24SKamil Horák (2N) 	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
413*03ab6c24SKamil Horák (2N) 	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
414*03ab6c24SKamil Horák (2N) 		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
415*03ab6c24SKamil Horák (2N) 		if (reg < 0)
416*03ab6c24SKamil Horák (2N) 			return reg;
417*03ab6c24SKamil Horák (2N) 		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
418*03ab6c24SKamil Horák (2N) 					BCM54612E_LED4_CLK125OUT_EN | reg);
419*03ab6c24SKamil Horák (2N) 		if (err < 0)
420*03ab6c24SKamil Horák (2N) 			return err;
421*03ab6c24SKamil Horák (2N) 	}
422*03ab6c24SKamil Horák (2N) 
423*03ab6c24SKamil Horák (2N) 	/* With BCM54811, BroadR-Reach implies no autoneg */
424*03ab6c24SKamil Horák (2N) 	if (priv->brr_mode)
425*03ab6c24SKamil Horák (2N) 		phydev->autoneg = 0;
426*03ab6c24SKamil Horák (2N) 
427*03ab6c24SKamil Horák (2N) 	return bcm5481x_set_brrmode(phydev, priv->brr_mode);
428*03ab6c24SKamil Horák (2N) }
429*03ab6c24SKamil Horák (2N) 
bcm54xx_config_init(struct phy_device * phydev)430c4b41c9fSMaciej W. Rozycki static int bcm54xx_config_init(struct phy_device *phydev)
431c4b41c9fSMaciej W. Rozycki {
43273333626SAbhishek Shah 	int reg, err, val;
433c4b41c9fSMaciej W. Rozycki 
434c4b41c9fSMaciej W. Rozycki 	reg = phy_read(phydev, MII_BCM54XX_ECR);
435c4b41c9fSMaciej W. Rozycki 	if (reg < 0)
436c4b41c9fSMaciej W. Rozycki 		return reg;
437c4b41c9fSMaciej W. Rozycki 
438c4b41c9fSMaciej W. Rozycki 	/* Mask interrupts globally.  */
439c4b41c9fSMaciej W. Rozycki 	reg |= MII_BCM54XX_ECR_IM;
440c4b41c9fSMaciej W. Rozycki 	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
441c4b41c9fSMaciej W. Rozycki 	if (err < 0)
442c4b41c9fSMaciej W. Rozycki 		return err;
443c4b41c9fSMaciej W. Rozycki 
444c4b41c9fSMaciej W. Rozycki 	/* Unmask events we are interested in.  */
445c4b41c9fSMaciej W. Rozycki 	reg = ~(MII_BCM54XX_INT_DUPLEX |
446c4b41c9fSMaciej W. Rozycki 		MII_BCM54XX_INT_SPEED |
447c4b41c9fSMaciej W. Rozycki 		MII_BCM54XX_INT_LINK);
448c4b41c9fSMaciej W. Rozycki 	err = phy_write(phydev, MII_BCM54XX_IMR, reg);
449c4b41c9fSMaciej W. Rozycki 	if (err < 0)
450c4b41c9fSMaciej W. Rozycki 		return err;
451772638b6SMatt Carlson 
45263a14ce4SMatt Carlson 	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
45363a14ce4SMatt Carlson 	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
45463a14ce4SMatt Carlson 	    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
455a1cba561SArun Parameswaran 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
45663a14ce4SMatt Carlson 
45732e5a8d6SMatt Carlson 	bcm54xx_adjust_rxrefclk(phydev);
45832e5a8d6SMatt Carlson 
4593afd0218SRobert Hancock 	switch (BRCM_PHY_MODEL(phydev)) {
460b1dd9bf6SFlorian Fainelli 	case PHY_ID_BCM50610:
461b1dd9bf6SFlorian Fainelli 	case PHY_ID_BCM50610M:
462b1dd9bf6SFlorian Fainelli 		err = bcm54xx_config_clock_delay(phydev);
463b1dd9bf6SFlorian Fainelli 		break;
4643afd0218SRobert Hancock 	case PHY_ID_BCM54210E:
4650fc9ae10SRafał Miłecki 		err = bcm54210e_config_init(phydev);
4663afd0218SRobert Hancock 		break;
4673afd0218SRobert Hancock 	case PHY_ID_BCM54612E:
46862e13097SRafał Miłecki 		err = bcm54612e_config_init(phydev);
4693afd0218SRobert Hancock 		break;
4703afd0218SRobert Hancock 	case PHY_ID_BCM54616S:
4713afd0218SRobert Hancock 		err = bcm54616s_config_init(phydev);
4723afd0218SRobert Hancock 		break;
4733afd0218SRobert Hancock 	case PHY_ID_BCM54810:
47473333626SAbhishek Shah 		/* For BCM54810, we need to disable BroadR-Reach function */
47573333626SAbhishek Shah 		val = bcm_phy_read_exp(phydev,
47673333626SAbhishek Shah 				       BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
47773333626SAbhishek Shah 		val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
47873333626SAbhishek Shah 		err = bcm_phy_write_exp(phydev,
47973333626SAbhishek Shah 					BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
48073333626SAbhishek Shah 					val);
4813afd0218SRobert Hancock 		break;
482*03ab6c24SKamil Horák (2N) 	case PHY_ID_BCM54811:
483*03ab6c24SKamil Horák (2N) 		err = bcm54811_config_init(phydev);
484*03ab6c24SKamil Horák (2N) 		break;
485b14995acSJon Mason 	}
4863afd0218SRobert Hancock 	if (err)
4873afd0218SRobert Hancock 		return err;
488b14995acSJon Mason 
48947b1b53bSMatt Carlson 	bcm54xx_phydsp_config(phydev);
490d9221e66SMatt Carlson 
491b5d007e2SRobert Hancock 	/* For non-SFP setups, encode link speed into LED1 and LED3 pair
492b5d007e2SRobert Hancock 	 * (green/amber).
493450895d0SVladimir Oltean 	 * Also flash these two LEDs on activity. This means configuring
494450895d0SVladimir Oltean 	 * them for MULTICOLOR and encoding link/activity into them.
495b5d007e2SRobert Hancock 	 * Don't do this for devices on an SFP module, since some of these
496b5d007e2SRobert Hancock 	 * use the LED outputs to control the SFP LOS signal, and changing
497b5d007e2SRobert Hancock 	 * these settings will cause LOS to malfunction.
498450895d0SVladimir Oltean 	 */
499b5d007e2SRobert Hancock 	if (!phy_on_sfp(phydev)) {
50057fd7d59SFlorian Fainelli 		val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
50157fd7d59SFlorian Fainelli 			BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
50257fd7d59SFlorian Fainelli 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val);
503450895d0SVladimir Oltean 
504450895d0SVladimir Oltean 		val = BCM_LED_MULTICOLOR_IN_PHASE |
50557fd7d59SFlorian Fainelli 			BCM54XX_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
50657fd7d59SFlorian Fainelli 			BCM54XX_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
507450895d0SVladimir Oltean 		bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
508b5d007e2SRobert Hancock 	}
509450895d0SVladimir Oltean 
51015acf89eSJonathan Lemon 	bcm54xx_ptp_config_init(phydev);
51115acf89eSJonathan Lemon 
5128baddaa9SFlorian Fainelli 	/* Acknowledge any left over interrupt and charge the device for
5138baddaa9SFlorian Fainelli 	 * wake-up.
5148baddaa9SFlorian Fainelli 	 */
5158baddaa9SFlorian Fainelli 	err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
5168baddaa9SFlorian Fainelli 	if (err < 0)
5178baddaa9SFlorian Fainelli 		return err;
5188baddaa9SFlorian Fainelli 
5198baddaa9SFlorian Fainelli 	if (err)
5208baddaa9SFlorian Fainelli 		pm_wakeup_event(&phydev->mdio.dev, 0);
5218baddaa9SFlorian Fainelli 
522c4b41c9fSMaciej W. Rozycki 	return 0;
523c4b41c9fSMaciej W. Rozycki }
524c4b41c9fSMaciej W. Rozycki 
bcm54xx_iddq_set(struct phy_device * phydev,bool enable)525d6da08edSFlorian Fainelli static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
526d6da08edSFlorian Fainelli {
527d6da08edSFlorian Fainelli 	int ret = 0;
528d6da08edSFlorian Fainelli 
529d6da08edSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND))
530d6da08edSFlorian Fainelli 		return ret;
531d6da08edSFlorian Fainelli 
532d6da08edSFlorian Fainelli 	ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL);
533d6da08edSFlorian Fainelli 	if (ret < 0)
534d6da08edSFlorian Fainelli 		goto out;
535d6da08edSFlorian Fainelli 
536d6da08edSFlorian Fainelli 	if (enable)
537d6da08edSFlorian Fainelli 		ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP;
538d6da08edSFlorian Fainelli 	else
539d6da08edSFlorian Fainelli 		ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP);
540d6da08edSFlorian Fainelli 
541d6da08edSFlorian Fainelli 	ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, ret);
542d6da08edSFlorian Fainelli out:
543d6da08edSFlorian Fainelli 	return ret;
544d6da08edSFlorian Fainelli }
545d6da08edSFlorian Fainelli 
bcm54xx_set_wakeup_irq(struct phy_device * phydev,bool state)5468baddaa9SFlorian Fainelli static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
5478baddaa9SFlorian Fainelli {
5488baddaa9SFlorian Fainelli 	struct bcm54xx_phy_priv *priv = phydev->priv;
5498baddaa9SFlorian Fainelli 	int ret = 0;
5508baddaa9SFlorian Fainelli 
5518baddaa9SFlorian Fainelli 	if (!bcm54xx_phy_can_wakeup(phydev))
5528baddaa9SFlorian Fainelli 		return ret;
5538baddaa9SFlorian Fainelli 
5548baddaa9SFlorian Fainelli 	if (priv->wake_irq_enabled != state) {
5558baddaa9SFlorian Fainelli 		if (state)
5568baddaa9SFlorian Fainelli 			ret = enable_irq_wake(priv->wake_irq);
5578baddaa9SFlorian Fainelli 		else
5588baddaa9SFlorian Fainelli 			ret = disable_irq_wake(priv->wake_irq);
5598baddaa9SFlorian Fainelli 		priv->wake_irq_enabled = state;
5608baddaa9SFlorian Fainelli 	}
5618baddaa9SFlorian Fainelli 
5628baddaa9SFlorian Fainelli 	return ret;
5638baddaa9SFlorian Fainelli }
5648baddaa9SFlorian Fainelli 
bcm54xx_suspend(struct phy_device * phydev)565d6da08edSFlorian Fainelli static int bcm54xx_suspend(struct phy_device *phydev)
566d6da08edSFlorian Fainelli {
5678baddaa9SFlorian Fainelli 	int ret = 0;
568d6da08edSFlorian Fainelli 
56915acf89eSJonathan Lemon 	bcm54xx_ptp_stop(phydev);
57015acf89eSJonathan Lemon 
5718baddaa9SFlorian Fainelli 	/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
5728baddaa9SFlorian Fainelli 	ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
5738baddaa9SFlorian Fainelli 	if (ret < 0)
5748baddaa9SFlorian Fainelli 		return ret;
5758baddaa9SFlorian Fainelli 
5768baddaa9SFlorian Fainelli 	if (phydev->wol_enabled)
5778baddaa9SFlorian Fainelli 		return bcm54xx_set_wakeup_irq(phydev, true);
5788baddaa9SFlorian Fainelli 
579d6da08edSFlorian Fainelli 	/* We cannot use a read/modify/write here otherwise the PHY gets into
580d6da08edSFlorian Fainelli 	 * a bad state where its LEDs keep flashing, thus defeating the purpose
581d6da08edSFlorian Fainelli 	 * of low power mode.
582d6da08edSFlorian Fainelli 	 */
583d6da08edSFlorian Fainelli 	ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
584d6da08edSFlorian Fainelli 	if (ret < 0)
585d6da08edSFlorian Fainelli 		return ret;
586d6da08edSFlorian Fainelli 
587d6da08edSFlorian Fainelli 	return bcm54xx_iddq_set(phydev, true);
588d6da08edSFlorian Fainelli }
589d6da08edSFlorian Fainelli 
bcm54xx_resume(struct phy_device * phydev)590fe26821fSFlorian Fainelli static int bcm54xx_resume(struct phy_device *phydev)
591fe26821fSFlorian Fainelli {
5928baddaa9SFlorian Fainelli 	int ret = 0;
5938baddaa9SFlorian Fainelli 
5948baddaa9SFlorian Fainelli 	if (phydev->wol_enabled) {
5958baddaa9SFlorian Fainelli 		ret = bcm54xx_set_wakeup_irq(phydev, false);
5968baddaa9SFlorian Fainelli 		if (ret)
5978baddaa9SFlorian Fainelli 			return ret;
5988baddaa9SFlorian Fainelli 	}
599fe26821fSFlorian Fainelli 
600d6da08edSFlorian Fainelli 	ret = bcm54xx_iddq_set(phydev, false);
601d6da08edSFlorian Fainelli 	if (ret < 0)
602d6da08edSFlorian Fainelli 		return ret;
603d6da08edSFlorian Fainelli 
604fe26821fSFlorian Fainelli 	/* Writes to register other than BMCR would be ignored
605fe26821fSFlorian Fainelli 	 * unless we clear the PDOWN bit first
606fe26821fSFlorian Fainelli 	 */
607fe26821fSFlorian Fainelli 	ret = genphy_resume(phydev);
608fe26821fSFlorian Fainelli 	if (ret < 0)
609fe26821fSFlorian Fainelli 		return ret;
610fe26821fSFlorian Fainelli 
6117a1468baSFlorian Fainelli 	/* Upon exiting power down, the PHY remains in an internal reset state
6127a1468baSFlorian Fainelli 	 * for 40us
6137a1468baSFlorian Fainelli 	 */
6147a1468baSFlorian Fainelli 	fsleep(40);
6157a1468baSFlorian Fainelli 
616d6da08edSFlorian Fainelli 	/* Issue a soft reset after clearing the power down bit
617d6da08edSFlorian Fainelli 	 * and before doing any other configuration.
618d6da08edSFlorian Fainelli 	 */
619d6da08edSFlorian Fainelli 	if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) {
620d6da08edSFlorian Fainelli 		ret = genphy_soft_reset(phydev);
621d6da08edSFlorian Fainelli 		if (ret < 0)
622d6da08edSFlorian Fainelli 			return ret;
623d6da08edSFlorian Fainelli 	}
624d6da08edSFlorian Fainelli 
625fe26821fSFlorian Fainelli 	return bcm54xx_config_init(phydev);
626fe26821fSFlorian Fainelli }
627fe26821fSFlorian Fainelli 
bcm54810_read_mmd(struct phy_device * phydev,int devnum,u16 regnum)628096516d0SJustin Chen static int bcm54810_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
629096516d0SJustin Chen {
630096516d0SJustin Chen 	return -EOPNOTSUPP;
631096516d0SJustin Chen }
632096516d0SJustin Chen 
bcm54810_write_mmd(struct phy_device * phydev,int devnum,u16 regnum,u16 val)633096516d0SJustin Chen static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
634096516d0SJustin Chen 			      u16 val)
635096516d0SJustin Chen {
636096516d0SJustin Chen 	return -EOPNOTSUPP;
637096516d0SJustin Chen }
638096516d0SJustin Chen 
639b0ed0bbfSKevin Lo 
640*03ab6c24SKamil Horák (2N) /**
641*03ab6c24SKamil Horák (2N)  * bcm5481x_read_abilities - read PHY abilities from LRESR or Clause 22
642*03ab6c24SKamil Horák (2N)  * (BMSR) registers, based on whether the PHY is in BroadR-Reach or IEEE mode
643*03ab6c24SKamil Horák (2N)  * @phydev: target phy_device struct
644*03ab6c24SKamil Horák (2N)  *
645*03ab6c24SKamil Horák (2N)  * Description: Reads the PHY's abilities and populates phydev->supported
646*03ab6c24SKamil Horák (2N)  * accordingly. The register to read the abilities from is determined by
647*03ab6c24SKamil Horák (2N)  * the brr mode setting of the PHY as read from the device tree.
648*03ab6c24SKamil Horák (2N)  * Note that the LRE and IEEE sets of abilities are disjunct, in other words,
649*03ab6c24SKamil Horák (2N)  * not only the link modes differ, but also the auto-negotiation and
650*03ab6c24SKamil Horák (2N)  * master-slave setup is controlled differently.
651*03ab6c24SKamil Horák (2N)  *
652*03ab6c24SKamil Horák (2N)  * Returns: 0 on success, < 0 on failure
653*03ab6c24SKamil Horák (2N)  */
bcm5481x_read_abilities(struct phy_device * phydev)654*03ab6c24SKamil Horák (2N) static int bcm5481x_read_abilities(struct phy_device *phydev)
65557bb7e22SAnton Vorontsov {
656b14995acSJon Mason 	struct device_node *np = phydev->mdio.dev.of_node;
657*03ab6c24SKamil Horák (2N) 	struct bcm54xx_phy_priv *priv = phydev->priv;
658*03ab6c24SKamil Horák (2N) 	int i, val, err;
65957bb7e22SAnton Vorontsov 
660*03ab6c24SKamil Horák (2N) 	for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
661*03ab6c24SKamil Horák (2N) 		linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
66257bb7e22SAnton Vorontsov 
663*03ab6c24SKamil Horák (2N) 	priv->brr_mode = of_property_read_bool(np, "brr-mode");
664*03ab6c24SKamil Horák (2N) 
665*03ab6c24SKamil Horák (2N) 	/* Set BroadR-Reach mode as configured in the DT. */
666*03ab6c24SKamil Horák (2N) 	err = bcm5481x_set_brrmode(phydev, priv->brr_mode);
667*03ab6c24SKamil Horák (2N) 	if (err)
668*03ab6c24SKamil Horák (2N) 		return err;
669*03ab6c24SKamil Horák (2N) 
670*03ab6c24SKamil Horák (2N) 	if (priv->brr_mode) {
671*03ab6c24SKamil Horák (2N) 		linkmode_set_bit_array(phy_basic_ports_array,
672*03ab6c24SKamil Horák (2N) 				       ARRAY_SIZE(phy_basic_ports_array),
673*03ab6c24SKamil Horák (2N) 				       phydev->supported);
674*03ab6c24SKamil Horák (2N) 
675*03ab6c24SKamil Horák (2N) 		val = phy_read(phydev, MII_BCM54XX_LRESR);
676*03ab6c24SKamil Horák (2N) 		if (val < 0)
677*03ab6c24SKamil Horák (2N) 			return val;
678*03ab6c24SKamil Horák (2N) 
679*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
680*03ab6c24SKamil Horák (2N) 				 phydev->supported,
681*03ab6c24SKamil Horák (2N) 				 val & LRESR_LDSABILITY);
682*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
683*03ab6c24SKamil Horák (2N) 				 phydev->supported,
684*03ab6c24SKamil Horák (2N) 				 val & LRESR_100_1PAIR);
685*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
686*03ab6c24SKamil Horák (2N) 				 phydev->supported,
687*03ab6c24SKamil Horák (2N) 				 val & LRESR_10_1PAIR);
688*03ab6c24SKamil Horák (2N) 		return 0;
689*03ab6c24SKamil Horák (2N) 	}
690*03ab6c24SKamil Horák (2N) 
691*03ab6c24SKamil Horák (2N) 	return genphy_read_abilities(phydev);
692*03ab6c24SKamil Horák (2N) }
693*03ab6c24SKamil Horák (2N) 
bcm5481x_config_delay_swap(struct phy_device * phydev)694*03ab6c24SKamil Horák (2N) static int bcm5481x_config_delay_swap(struct phy_device *phydev)
695*03ab6c24SKamil Horák (2N) {
696*03ab6c24SKamil Horák (2N) 	struct device_node *np = phydev->mdio.dev.of_node;
697*03ab6c24SKamil Horák (2N) 
698*03ab6c24SKamil Horák (2N) 	/* Set up the delay. */
699042cb564STao Ren 	bcm54xx_config_clock_delay(phydev);
70057bb7e22SAnton Vorontsov 
701b14995acSJon Mason 	if (of_property_read_bool(np, "enet-phy-lane-swap")) {
702b14995acSJon Mason 		/* Lane Swap - Undocumented register...magic! */
703*03ab6c24SKamil Horák (2N) 		int ret = bcm_phy_write_exp(phydev,
704*03ab6c24SKamil Horák (2N) 					    MII_BCM54XX_EXP_SEL_ER + 0x9,
705b14995acSJon Mason 					    0x11B);
706b14995acSJon Mason 		if (ret < 0)
707b14995acSJon Mason 			return ret;
708b14995acSJon Mason 	}
709b14995acSJon Mason 
710*03ab6c24SKamil Horák (2N) 	return 0;
711*03ab6c24SKamil Horák (2N) }
712*03ab6c24SKamil Horák (2N) 
bcm5481_config_aneg(struct phy_device * phydev)713*03ab6c24SKamil Horák (2N) static int bcm5481_config_aneg(struct phy_device *phydev)
714*03ab6c24SKamil Horák (2N) {
715*03ab6c24SKamil Horák (2N) 	struct bcm54xx_phy_priv *priv = phydev->priv;
716*03ab6c24SKamil Horák (2N) 	int ret;
717*03ab6c24SKamil Horák (2N) 
718*03ab6c24SKamil Horák (2N) 	/* Aneg firstly. */
719*03ab6c24SKamil Horák (2N) 	if (priv->brr_mode)
720*03ab6c24SKamil Horák (2N) 		ret = bcm_config_lre_aneg(phydev, false);
721*03ab6c24SKamil Horák (2N) 	else
722*03ab6c24SKamil Horák (2N) 		ret = genphy_config_aneg(phydev);
723*03ab6c24SKamil Horák (2N) 
724*03ab6c24SKamil Horák (2N) 	if (ret)
72557bb7e22SAnton Vorontsov 		return ret;
726*03ab6c24SKamil Horák (2N) 
727*03ab6c24SKamil Horák (2N) 	/* Then we can set up the delay and swap. */
728*03ab6c24SKamil Horák (2N) 	return bcm5481x_config_delay_swap(phydev);
729*03ab6c24SKamil Horák (2N) }
730*03ab6c24SKamil Horák (2N) 
bcm54811_config_aneg(struct phy_device * phydev)731*03ab6c24SKamil Horák (2N) static int bcm54811_config_aneg(struct phy_device *phydev)
732*03ab6c24SKamil Horák (2N) {
733*03ab6c24SKamil Horák (2N) 	struct bcm54xx_phy_priv *priv = phydev->priv;
734*03ab6c24SKamil Horák (2N) 	int ret;
735*03ab6c24SKamil Horák (2N) 
736*03ab6c24SKamil Horák (2N) 	/* Aneg firstly. */
737*03ab6c24SKamil Horák (2N) 	if (priv->brr_mode) {
738*03ab6c24SKamil Horák (2N) 		/* BCM54811 is only capable of autonegotiation in IEEE mode */
739*03ab6c24SKamil Horák (2N) 		phydev->autoneg = 0;
740*03ab6c24SKamil Horák (2N) 		ret = bcm_config_lre_aneg(phydev, false);
741*03ab6c24SKamil Horák (2N) 	} else {
742*03ab6c24SKamil Horák (2N) 		ret = genphy_config_aneg(phydev);
743*03ab6c24SKamil Horák (2N) 	}
744*03ab6c24SKamil Horák (2N) 
745*03ab6c24SKamil Horák (2N) 	if (ret)
746*03ab6c24SKamil Horák (2N) 		return ret;
747*03ab6c24SKamil Horák (2N) 
748*03ab6c24SKamil Horák (2N) 	/* Then we can set up the delay and swap. */
749*03ab6c24SKamil Horák (2N) 	return bcm5481x_config_delay_swap(phydev);
75057bb7e22SAnton Vorontsov }
75157bb7e22SAnton Vorontsov 
75217d3a83aSFlorian Fainelli struct bcm54616s_phy_priv {
75317d3a83aSFlorian Fainelli 	bool mode_1000bx_en;
75417d3a83aSFlorian Fainelli };
75517d3a83aSFlorian Fainelli 
bcm54616s_probe(struct phy_device * phydev)756b9bcb953STao Ren static int bcm54616s_probe(struct phy_device *phydev)
757b9bcb953STao Ren {
75817d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv;
7593afd0218SRobert Hancock 	int val;
760b9bcb953STao Ren 
76117d3a83aSFlorian Fainelli 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
76217d3a83aSFlorian Fainelli 	if (!priv)
76317d3a83aSFlorian Fainelli 		return -ENOMEM;
76417d3a83aSFlorian Fainelli 
76517d3a83aSFlorian Fainelli 	phydev->priv = priv;
76617d3a83aSFlorian Fainelli 
767b9bcb953STao Ren 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
768b9bcb953STao Ren 	if (val < 0)
769b9bcb953STao Ren 		return val;
770b9bcb953STao Ren 
771b9bcb953STao Ren 	/* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0]
772b9bcb953STao Ren 	 * is 01b, and the link between PHY and its link partner can be
773b9bcb953STao Ren 	 * either 1000Base-X or 100Base-FX.
774b9bcb953STao Ren 	 * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
775b9bcb953STao Ren 	 * support is still missing as of now.
776b9bcb953STao Ren 	 */
7773afd0218SRobert Hancock 	if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
778b9bcb953STao Ren 		val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL);
779b9bcb953STao Ren 		if (val < 0)
780b9bcb953STao Ren 			return val;
781b9bcb953STao Ren 
782b9bcb953STao Ren 		/* Bit 0 of the SerDes 100-FX Control register, when set
783b9bcb953STao Ren 		 * to 1, sets the MII/RGMII -> 100BASE-FX configuration.
784b9bcb953STao Ren 		 * When this bit is set to 0, it sets the GMII/RGMII ->
785b9bcb953STao Ren 		 * 1000BASE-X configuration.
786b9bcb953STao Ren 		 */
787b9bcb953STao Ren 		if (!(val & BCM54616S_100FX_MODE))
78817d3a83aSFlorian Fainelli 			priv->mode_1000bx_en = true;
7894217a64eSMichael Walle 
7904217a64eSMichael Walle 		phydev->port = PORT_FIBRE;
791b9bcb953STao Ren 	}
792b9bcb953STao Ren 
793b9bcb953STao Ren 	return 0;
794b9bcb953STao Ren }
795b9bcb953STao Ren 
bcm54616s_config_aneg(struct phy_device * phydev)796042cb564STao Ren static int bcm54616s_config_aneg(struct phy_device *phydev)
797042cb564STao Ren {
79817d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv = phydev->priv;
799042cb564STao Ren 	int ret;
800042cb564STao Ren 
80129f20dd6SJonathan Neuschäfer 	/* Aneg firstly. */
80217d3a83aSFlorian Fainelli 	if (priv->mode_1000bx_en)
803b9bcb953STao Ren 		ret = genphy_c37_config_aneg(phydev);
804b9bcb953STao Ren 	else
805042cb564STao Ren 		ret = genphy_config_aneg(phydev);
806042cb564STao Ren 
807042cb564STao Ren 	/* Then we can set up the delay. */
808042cb564STao Ren 	bcm54xx_config_clock_delay(phydev);
809042cb564STao Ren 
810042cb564STao Ren 	return ret;
811042cb564STao Ren }
812042cb564STao Ren 
bcm54616s_read_status(struct phy_device * phydev)813b9bcb953STao Ren static int bcm54616s_read_status(struct phy_device *phydev)
814b9bcb953STao Ren {
81517d3a83aSFlorian Fainelli 	struct bcm54616s_phy_priv *priv = phydev->priv;
8169b1d5e05SChristian Marangi 	bool changed;
817b9bcb953STao Ren 	int err;
818b9bcb953STao Ren 
81917d3a83aSFlorian Fainelli 	if (priv->mode_1000bx_en)
8209b1d5e05SChristian Marangi 		err = genphy_c37_read_status(phydev, &changed);
821b9bcb953STao Ren 	else
822b9bcb953STao Ren 		err = genphy_read_status(phydev);
823b9bcb953STao Ren 
824b9bcb953STao Ren 	return err;
825b9bcb953STao Ren }
826b9bcb953STao Ren 
brcm_fet_config_init(struct phy_device * phydev)827d7a2ed92SMatt Carlson static int brcm_fet_config_init(struct phy_device *phydev)
828d7a2ed92SMatt Carlson {
829d7a2ed92SMatt Carlson 	int reg, err, err2, brcmtest;
830d7a2ed92SMatt Carlson 
831d7a2ed92SMatt Carlson 	/* Reset the PHY to bring it to a known state. */
832d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
833d7a2ed92SMatt Carlson 	if (err < 0)
834d7a2ed92SMatt Carlson 		return err;
835d7a2ed92SMatt Carlson 
836bf8bfc43SFlorian Fainelli 	/* The datasheet indicates the PHY needs up to 1us to complete a reset,
837bf8bfc43SFlorian Fainelli 	 * build some slack here.
838bf8bfc43SFlorian Fainelli 	 */
839bf8bfc43SFlorian Fainelli 	usleep_range(1000, 2000);
840bf8bfc43SFlorian Fainelli 
841bf8bfc43SFlorian Fainelli 	/* The PHY requires 65 MDC clock cycles to complete a write operation
842bf8bfc43SFlorian Fainelli 	 * and turnaround the line properly.
843bf8bfc43SFlorian Fainelli 	 *
844bf8bfc43SFlorian Fainelli 	 * We ignore -EIO here as the MDIO controller (e.g.: mdio-bcm-unimac)
845bf8bfc43SFlorian Fainelli 	 * may flag the lack of turn-around as a read failure. This is
846bf8bfc43SFlorian Fainelli 	 * particularly true with this combination since the MDIO controller
847bf8bfc43SFlorian Fainelli 	 * only used 64 MDC cycles. This is not a critical failure in this
848bf8bfc43SFlorian Fainelli 	 * specific case and it has no functional impact otherwise, so we let
849bf8bfc43SFlorian Fainelli 	 * that one go through. If there is a genuine bus error, the next read
850bf8bfc43SFlorian Fainelli 	 * of MII_BRCM_FET_INTREG will error out.
851bf8bfc43SFlorian Fainelli 	 */
852bf8bfc43SFlorian Fainelli 	err = phy_read(phydev, MII_BMCR);
853bf8bfc43SFlorian Fainelli 	if (err < 0 && err != -EIO)
854bf8bfc43SFlorian Fainelli 		return err;
855bf8bfc43SFlorian Fainelli 
8563abbd069SGiulio Benetti 	/* Read to clear status bits */
857d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
858d7a2ed92SMatt Carlson 	if (reg < 0)
859d7a2ed92SMatt Carlson 		return reg;
860d7a2ed92SMatt Carlson 
861d7a2ed92SMatt Carlson 	/* Unmask events we are interested in and mask interrupts globally. */
8623abbd069SGiulio Benetti 	if (phydev->phy_id == PHY_ID_BCM5221)
8633abbd069SGiulio Benetti 		reg = MII_BRCM_FET_IR_ENABLE |
8643abbd069SGiulio Benetti 		      MII_BRCM_FET_IR_MASK;
8653abbd069SGiulio Benetti 	else
866d7a2ed92SMatt Carlson 		reg = MII_BRCM_FET_IR_DUPLEX_EN |
867d7a2ed92SMatt Carlson 		      MII_BRCM_FET_IR_SPEED_EN |
868d7a2ed92SMatt Carlson 		      MII_BRCM_FET_IR_LINK_EN |
869d7a2ed92SMatt Carlson 		      MII_BRCM_FET_IR_ENABLE |
870d7a2ed92SMatt Carlson 		      MII_BRCM_FET_IR_MASK;
871d7a2ed92SMatt Carlson 
872d7a2ed92SMatt Carlson 	err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
873d7a2ed92SMatt Carlson 	if (err < 0)
874d7a2ed92SMatt Carlson 		return err;
875d7a2ed92SMatt Carlson 
876d7a2ed92SMatt Carlson 	/* Enable shadow register access */
877d7a2ed92SMatt Carlson 	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
878d7a2ed92SMatt Carlson 	if (brcmtest < 0)
879d7a2ed92SMatt Carlson 		return brcmtest;
880d7a2ed92SMatt Carlson 
881d7a2ed92SMatt Carlson 	reg = brcmtest | MII_BRCM_FET_BT_SRE;
882d7a2ed92SMatt Carlson 
8833abbd069SGiulio Benetti 	phy_lock_mdio_bus(phydev);
884d7a2ed92SMatt Carlson 
8853abbd069SGiulio Benetti 	err = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
8863abbd069SGiulio Benetti 	if (err < 0) {
8873abbd069SGiulio Benetti 		phy_unlock_mdio_bus(phydev);
8883abbd069SGiulio Benetti 		return err;
8893abbd069SGiulio Benetti 	}
8903abbd069SGiulio Benetti 
8913abbd069SGiulio Benetti 	if (phydev->phy_id != PHY_ID_BCM5221) {
892d7a2ed92SMatt Carlson 		/* Set the LED mode */
8933abbd069SGiulio Benetti 		reg = __phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
894d7a2ed92SMatt Carlson 		if (reg < 0) {
895d7a2ed92SMatt Carlson 			err = reg;
896d7a2ed92SMatt Carlson 			goto done;
897d7a2ed92SMatt Carlson 		}
898d7a2ed92SMatt Carlson 
8993abbd069SGiulio Benetti 		err = __phy_modify(phydev, MII_BRCM_FET_SHDW_AUXMODE4,
9003abbd069SGiulio Benetti 				   MII_BRCM_FET_SHDW_AM4_LED_MASK,
9013abbd069SGiulio Benetti 				   MII_BRCM_FET_SHDW_AM4_LED_MODE1);
902d7a2ed92SMatt Carlson 		if (err < 0)
903d7a2ed92SMatt Carlson 			goto done;
904d7a2ed92SMatt Carlson 
905d7a2ed92SMatt Carlson 		/* Enable auto MDIX */
9063abbd069SGiulio Benetti 		err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
907d7a2ed92SMatt Carlson 				     MII_BRCM_FET_SHDW_MC_FAME);
908d7a2ed92SMatt Carlson 		if (err < 0)
909d7a2ed92SMatt Carlson 			goto done;
9103abbd069SGiulio Benetti 	}
911d7a2ed92SMatt Carlson 
912cdd4e09dSMatt Carlson 	if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
913d7a2ed92SMatt Carlson 		/* Enable auto power down */
9143abbd069SGiulio Benetti 		err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
915d7a2ed92SMatt Carlson 				     MII_BRCM_FET_SHDW_AS2_APDE);
916cdd4e09dSMatt Carlson 	}
917d7a2ed92SMatt Carlson 
918d7a2ed92SMatt Carlson done:
919d7a2ed92SMatt Carlson 	/* Disable shadow register access */
9203abbd069SGiulio Benetti 	err2 = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
921d7a2ed92SMatt Carlson 	if (!err)
922d7a2ed92SMatt Carlson 		err = err2;
923d7a2ed92SMatt Carlson 
9243abbd069SGiulio Benetti 	phy_unlock_mdio_bus(phydev);
9253abbd069SGiulio Benetti 
926d7a2ed92SMatt Carlson 	return err;
927d7a2ed92SMatt Carlson }
928d7a2ed92SMatt Carlson 
brcm_fet_ack_interrupt(struct phy_device * phydev)929d7a2ed92SMatt Carlson static int brcm_fet_ack_interrupt(struct phy_device *phydev)
930d7a2ed92SMatt Carlson {
931d7a2ed92SMatt Carlson 	int reg;
932d7a2ed92SMatt Carlson 
933d7a2ed92SMatt Carlson 	/* Clear pending interrupts.  */
934d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
935d7a2ed92SMatt Carlson 	if (reg < 0)
936d7a2ed92SMatt Carlson 		return reg;
937d7a2ed92SMatt Carlson 
938d7a2ed92SMatt Carlson 	return 0;
939d7a2ed92SMatt Carlson }
940d7a2ed92SMatt Carlson 
brcm_fet_config_intr(struct phy_device * phydev)941d7a2ed92SMatt Carlson static int brcm_fet_config_intr(struct phy_device *phydev)
942d7a2ed92SMatt Carlson {
943d7a2ed92SMatt Carlson 	int reg, err;
944d7a2ed92SMatt Carlson 
945d7a2ed92SMatt Carlson 	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
946d7a2ed92SMatt Carlson 	if (reg < 0)
947d7a2ed92SMatt Carlson 		return reg;
948d7a2ed92SMatt Carlson 
94915772e4dSIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
95015772e4dSIoana Ciornei 		err = brcm_fet_ack_interrupt(phydev);
95115772e4dSIoana Ciornei 		if (err)
95215772e4dSIoana Ciornei 			return err;
953d7a2ed92SMatt Carlson 
95415772e4dSIoana Ciornei 		reg &= ~MII_BRCM_FET_IR_MASK;
955d7a2ed92SMatt Carlson 		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
95615772e4dSIoana Ciornei 	} else {
95715772e4dSIoana Ciornei 		reg |= MII_BRCM_FET_IR_MASK;
95815772e4dSIoana Ciornei 		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
95915772e4dSIoana Ciornei 		if (err)
96015772e4dSIoana Ciornei 			return err;
96115772e4dSIoana Ciornei 
96215772e4dSIoana Ciornei 		err = brcm_fet_ack_interrupt(phydev);
96315772e4dSIoana Ciornei 	}
96415772e4dSIoana Ciornei 
965d7a2ed92SMatt Carlson 	return err;
966d7a2ed92SMatt Carlson }
967d7a2ed92SMatt Carlson 
brcm_fet_handle_interrupt(struct phy_device * phydev)9684567d5c3SIoana Ciornei static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev)
9694567d5c3SIoana Ciornei {
9704567d5c3SIoana Ciornei 	int irq_status;
9714567d5c3SIoana Ciornei 
9724567d5c3SIoana Ciornei 	irq_status = phy_read(phydev, MII_BRCM_FET_INTREG);
9734567d5c3SIoana Ciornei 	if (irq_status < 0) {
9744567d5c3SIoana Ciornei 		phy_error(phydev);
9754567d5c3SIoana Ciornei 		return IRQ_NONE;
9764567d5c3SIoana Ciornei 	}
9774567d5c3SIoana Ciornei 
9784567d5c3SIoana Ciornei 	if (irq_status == 0)
9794567d5c3SIoana Ciornei 		return IRQ_NONE;
9804567d5c3SIoana Ciornei 
9814567d5c3SIoana Ciornei 	phy_trigger_machine(phydev);
9824567d5c3SIoana Ciornei 
9834567d5c3SIoana Ciornei 	return IRQ_HANDLED;
9844567d5c3SIoana Ciornei }
9854567d5c3SIoana Ciornei 
brcm_fet_suspend(struct phy_device * phydev)9860630f64dSFlorian Fainelli static int brcm_fet_suspend(struct phy_device *phydev)
9870630f64dSFlorian Fainelli {
9880630f64dSFlorian Fainelli 	int reg, err, err2, brcmtest;
9890630f64dSFlorian Fainelli 
9900630f64dSFlorian Fainelli 	/* We cannot use a read/modify/write here otherwise the PHY continues
9910630f64dSFlorian Fainelli 	 * to drive LEDs which defeats the purpose of low power mode.
9920630f64dSFlorian Fainelli 	 */
9930630f64dSFlorian Fainelli 	err = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
9940630f64dSFlorian Fainelli 	if (err < 0)
9950630f64dSFlorian Fainelli 		return err;
9960630f64dSFlorian Fainelli 
9970630f64dSFlorian Fainelli 	/* Enable shadow register access */
9980630f64dSFlorian Fainelli 	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
9990630f64dSFlorian Fainelli 	if (brcmtest < 0)
10000630f64dSFlorian Fainelli 		return brcmtest;
10010630f64dSFlorian Fainelli 
10020630f64dSFlorian Fainelli 	reg = brcmtest | MII_BRCM_FET_BT_SRE;
10030630f64dSFlorian Fainelli 
10043abbd069SGiulio Benetti 	phy_lock_mdio_bus(phydev);
10050630f64dSFlorian Fainelli 
10063abbd069SGiulio Benetti 	err = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
10073abbd069SGiulio Benetti 	if (err < 0) {
10083abbd069SGiulio Benetti 		phy_unlock_mdio_bus(phydev);
10093abbd069SGiulio Benetti 		return err;
10103abbd069SGiulio Benetti 	}
10113abbd069SGiulio Benetti 
10123abbd069SGiulio Benetti 	if (phydev->phy_id == PHY_ID_BCM5221)
10133abbd069SGiulio Benetti 		/* Force Low Power Mode with clock enabled */
10143abbd069SGiulio Benetti 		reg = BCM5221_SHDW_AM4_EN_CLK_LPM | BCM5221_SHDW_AM4_FORCE_LPM;
10153abbd069SGiulio Benetti 	else
10160630f64dSFlorian Fainelli 		/* Set standby mode */
10173abbd069SGiulio Benetti 		reg = MII_BRCM_FET_SHDW_AM4_STANDBY;
10183abbd069SGiulio Benetti 
10193abbd069SGiulio Benetti 	err = __phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
10200630f64dSFlorian Fainelli 
10210630f64dSFlorian Fainelli 	/* Disable shadow register access */
10223abbd069SGiulio Benetti 	err2 = __phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
10230630f64dSFlorian Fainelli 	if (!err)
10240630f64dSFlorian Fainelli 		err = err2;
10250630f64dSFlorian Fainelli 
10263abbd069SGiulio Benetti 	phy_unlock_mdio_bus(phydev);
10273abbd069SGiulio Benetti 
10280630f64dSFlorian Fainelli 	return err;
10290630f64dSFlorian Fainelli }
10300630f64dSFlorian Fainelli 
bcm5221_config_aneg(struct phy_device * phydev)10313abbd069SGiulio Benetti static int bcm5221_config_aneg(struct phy_device *phydev)
10323abbd069SGiulio Benetti {
10333abbd069SGiulio Benetti 	int ret, val;
10343abbd069SGiulio Benetti 
10353abbd069SGiulio Benetti 	ret = genphy_config_aneg(phydev);
10363abbd069SGiulio Benetti 	if (ret)
10373abbd069SGiulio Benetti 		return ret;
10383abbd069SGiulio Benetti 
10393abbd069SGiulio Benetti 	switch (phydev->mdix_ctrl) {
10403abbd069SGiulio Benetti 	case ETH_TP_MDI:
10413abbd069SGiulio Benetti 		val = BCM5221_AEGSR_MDIX_DIS;
10423abbd069SGiulio Benetti 		break;
10433abbd069SGiulio Benetti 	case ETH_TP_MDI_X:
10443abbd069SGiulio Benetti 		val = BCM5221_AEGSR_MDIX_DIS | BCM5221_AEGSR_MDIX_MAN_SWAP;
10453abbd069SGiulio Benetti 		break;
10463abbd069SGiulio Benetti 	case ETH_TP_MDI_AUTO:
10473abbd069SGiulio Benetti 		val = 0;
10483abbd069SGiulio Benetti 		break;
10493abbd069SGiulio Benetti 	default:
10503abbd069SGiulio Benetti 		return 0;
10513abbd069SGiulio Benetti 	}
10523abbd069SGiulio Benetti 
10533abbd069SGiulio Benetti 	return phy_modify(phydev, BCM5221_AEGSR, BCM5221_AEGSR_MDIX_MAN_SWAP |
10543abbd069SGiulio Benetti 						 BCM5221_AEGSR_MDIX_DIS,
10553abbd069SGiulio Benetti 						 val);
10563abbd069SGiulio Benetti }
10573abbd069SGiulio Benetti 
bcm5221_read_status(struct phy_device * phydev)10583abbd069SGiulio Benetti static int bcm5221_read_status(struct phy_device *phydev)
10593abbd069SGiulio Benetti {
10603abbd069SGiulio Benetti 	int ret;
10613abbd069SGiulio Benetti 
10623abbd069SGiulio Benetti 	/* Read MDIX status */
10633abbd069SGiulio Benetti 	ret = phy_read(phydev, BCM5221_AEGSR);
10643abbd069SGiulio Benetti 	if (ret < 0)
10653abbd069SGiulio Benetti 		return ret;
10663abbd069SGiulio Benetti 
10673abbd069SGiulio Benetti 	if (ret & BCM5221_AEGSR_MDIX_DIS) {
10683abbd069SGiulio Benetti 		if (ret & BCM5221_AEGSR_MDIX_MAN_SWAP)
10693abbd069SGiulio Benetti 			phydev->mdix_ctrl = ETH_TP_MDI_X;
10703abbd069SGiulio Benetti 		else
10713abbd069SGiulio Benetti 			phydev->mdix_ctrl = ETH_TP_MDI;
10723abbd069SGiulio Benetti 	} else {
10733abbd069SGiulio Benetti 		phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
10743abbd069SGiulio Benetti 	}
10753abbd069SGiulio Benetti 
10763abbd069SGiulio Benetti 	if (ret & BCM5221_AEGSR_MDIX_STATUS)
10773abbd069SGiulio Benetti 		phydev->mdix = ETH_TP_MDI_X;
10783abbd069SGiulio Benetti 	else
10793abbd069SGiulio Benetti 		phydev->mdix = ETH_TP_MDI;
10803abbd069SGiulio Benetti 
10813abbd069SGiulio Benetti 	return genphy_read_status(phydev);
10823abbd069SGiulio Benetti }
10833abbd069SGiulio Benetti 
bcm54xx_phy_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)10848baddaa9SFlorian Fainelli static void bcm54xx_phy_get_wol(struct phy_device *phydev,
10858baddaa9SFlorian Fainelli 				struct ethtool_wolinfo *wol)
10868baddaa9SFlorian Fainelli {
10878baddaa9SFlorian Fainelli 	/* We cannot wake-up if we do not have a dedicated PHY interrupt line
10888baddaa9SFlorian Fainelli 	 * or an out of band GPIO descriptor for wake-up. Zeroing
10898baddaa9SFlorian Fainelli 	 * wol->supported allows the caller (MAC driver) to play through and
10908baddaa9SFlorian Fainelli 	 * offer its own Wake-on-LAN scheme if available.
10918baddaa9SFlorian Fainelli 	 */
10928baddaa9SFlorian Fainelli 	if (!bcm54xx_phy_can_wakeup(phydev)) {
10938baddaa9SFlorian Fainelli 		wol->supported = 0;
10948baddaa9SFlorian Fainelli 		return;
10958baddaa9SFlorian Fainelli 	}
10968baddaa9SFlorian Fainelli 
10978baddaa9SFlorian Fainelli 	bcm_phy_get_wol(phydev, wol);
10988baddaa9SFlorian Fainelli }
10998baddaa9SFlorian Fainelli 
bcm54xx_phy_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)11008baddaa9SFlorian Fainelli static int bcm54xx_phy_set_wol(struct phy_device *phydev,
11018baddaa9SFlorian Fainelli 			       struct ethtool_wolinfo *wol)
11028baddaa9SFlorian Fainelli {
11038baddaa9SFlorian Fainelli 	int ret;
11048baddaa9SFlorian Fainelli 
11058baddaa9SFlorian Fainelli 	/* We cannot wake-up if we do not have a dedicated PHY interrupt line
11068baddaa9SFlorian Fainelli 	 * or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP
11078baddaa9SFlorian Fainelli 	 * allows the caller (MAC driver) to play through and offer its own
11088baddaa9SFlorian Fainelli 	 * Wake-on-LAN scheme if available.
11098baddaa9SFlorian Fainelli 	 */
11108baddaa9SFlorian Fainelli 	if (!bcm54xx_phy_can_wakeup(phydev))
11118baddaa9SFlorian Fainelli 		return -EOPNOTSUPP;
11128baddaa9SFlorian Fainelli 
11138baddaa9SFlorian Fainelli 	ret = bcm_phy_set_wol(phydev, wol);
11148baddaa9SFlorian Fainelli 	if (ret < 0)
11158baddaa9SFlorian Fainelli 		return ret;
11168baddaa9SFlorian Fainelli 
11178baddaa9SFlorian Fainelli 	return 0;
11188baddaa9SFlorian Fainelli }
11198baddaa9SFlorian Fainelli 
bcm54xx_phy_probe(struct phy_device * phydev)11205a32fcdbSFlorian Fainelli static int bcm54xx_phy_probe(struct phy_device *phydev)
112128dc4c8fSFlorian Fainelli {
11225a32fcdbSFlorian Fainelli 	struct bcm54xx_phy_priv *priv;
11238baddaa9SFlorian Fainelli 	struct gpio_desc *wakeup_gpio;
11248baddaa9SFlorian Fainelli 	int ret = 0;
112528dc4c8fSFlorian Fainelli 
112628dc4c8fSFlorian Fainelli 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
112728dc4c8fSFlorian Fainelli 	if (!priv)
112828dc4c8fSFlorian Fainelli 		return -ENOMEM;
112928dc4c8fSFlorian Fainelli 
11308baddaa9SFlorian Fainelli 	priv->wake_irq = -ENXIO;
11318baddaa9SFlorian Fainelli 
113228dc4c8fSFlorian Fainelli 	phydev->priv = priv;
113328dc4c8fSFlorian Fainelli 
113428dc4c8fSFlorian Fainelli 	priv->stats = devm_kcalloc(&phydev->mdio.dev,
113528dc4c8fSFlorian Fainelli 				   bcm_phy_get_sset_count(phydev), sizeof(u64),
113628dc4c8fSFlorian Fainelli 				   GFP_KERNEL);
113728dc4c8fSFlorian Fainelli 	if (!priv->stats)
113828dc4c8fSFlorian Fainelli 		return -ENOMEM;
113928dc4c8fSFlorian Fainelli 
114015acf89eSJonathan Lemon 	priv->ptp = bcm_ptp_probe(phydev);
114115acf89eSJonathan Lemon 	if (IS_ERR(priv->ptp))
114215acf89eSJonathan Lemon 		return PTR_ERR(priv->ptp);
114315acf89eSJonathan Lemon 
11448baddaa9SFlorian Fainelli 	/* We cannot utilize the _optional variant here since we want to know
11458baddaa9SFlorian Fainelli 	 * whether the GPIO descriptor exists or not to advertise Wake-on-LAN
11468baddaa9SFlorian Fainelli 	 * support or not.
11478baddaa9SFlorian Fainelli 	 */
11488baddaa9SFlorian Fainelli 	wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN);
11498baddaa9SFlorian Fainelli 	if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER)
11508baddaa9SFlorian Fainelli 		return PTR_ERR(wakeup_gpio);
11518baddaa9SFlorian Fainelli 
11528baddaa9SFlorian Fainelli 	if (!IS_ERR(wakeup_gpio)) {
11538baddaa9SFlorian Fainelli 		priv->wake_irq = gpiod_to_irq(wakeup_gpio);
11544781e965SFlorian Fainelli 
11554781e965SFlorian Fainelli 		/* Dummy interrupt handler which is not enabled but is provided
11564781e965SFlorian Fainelli 		 * in order for the interrupt descriptor to be fully set-up.
11574781e965SFlorian Fainelli 		 */
11584781e965SFlorian Fainelli 		ret = devm_request_irq(&phydev->mdio.dev, priv->wake_irq,
11594781e965SFlorian Fainelli 				       bcm_phy_wol_isr,
11604781e965SFlorian Fainelli 				       IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN,
11614781e965SFlorian Fainelli 				       dev_name(&phydev->mdio.dev), phydev);
11628baddaa9SFlorian Fainelli 		if (ret)
11638baddaa9SFlorian Fainelli 			return ret;
11648baddaa9SFlorian Fainelli 	}
11658baddaa9SFlorian Fainelli 
11668baddaa9SFlorian Fainelli 	/* If we do not have a main interrupt or a side-band wake-up interrupt,
11678baddaa9SFlorian Fainelli 	 * then the device cannot be marked as wake-up capable.
11688baddaa9SFlorian Fainelli 	 */
11698baddaa9SFlorian Fainelli 	if (!bcm54xx_phy_can_wakeup(phydev))
117028dc4c8fSFlorian Fainelli 		return 0;
11718baddaa9SFlorian Fainelli 
11728baddaa9SFlorian Fainelli 	return device_init_wakeup(&phydev->mdio.dev, true);
117328dc4c8fSFlorian Fainelli }
117428dc4c8fSFlorian Fainelli 
bcm54xx_get_stats(struct phy_device * phydev,struct ethtool_stats * stats,u64 * data)11755a32fcdbSFlorian Fainelli static void bcm54xx_get_stats(struct phy_device *phydev,
117628dc4c8fSFlorian Fainelli 			      struct ethtool_stats *stats, u64 *data)
117728dc4c8fSFlorian Fainelli {
11785a32fcdbSFlorian Fainelli 	struct bcm54xx_phy_priv *priv = phydev->priv;
117928dc4c8fSFlorian Fainelli 
118028dc4c8fSFlorian Fainelli 	bcm_phy_get_stats(phydev, priv->stats, stats, data);
118128dc4c8fSFlorian Fainelli }
118228dc4c8fSFlorian Fainelli 
bcm54xx_link_change_notify(struct phy_device * phydev)11838dc84dcdSFlorian Fainelli static void bcm54xx_link_change_notify(struct phy_device *phydev)
11848dc84dcdSFlorian Fainelli {
11858dc84dcdSFlorian Fainelli 	u16 mask = MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE |
11868dc84dcdSFlorian Fainelli 		   MII_BCM54XX_EXP_EXP08_FORCE_DAC_WAKE;
11878dc84dcdSFlorian Fainelli 	int ret;
11888dc84dcdSFlorian Fainelli 
11898dc84dcdSFlorian Fainelli 	if (phydev->state != PHY_RUNNING)
11908dc84dcdSFlorian Fainelli 		return;
11918dc84dcdSFlorian Fainelli 
11928dc84dcdSFlorian Fainelli 	/* Don't change the DAC wake settings if auto power down
11938dc84dcdSFlorian Fainelli 	 * is not requested.
11948dc84dcdSFlorian Fainelli 	 */
11958dc84dcdSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
11968dc84dcdSFlorian Fainelli 		return;
11978dc84dcdSFlorian Fainelli 
11988dc84dcdSFlorian Fainelli 	ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08);
11998dc84dcdSFlorian Fainelli 	if (ret < 0)
12008dc84dcdSFlorian Fainelli 		return;
12018dc84dcdSFlorian Fainelli 
12028dc84dcdSFlorian Fainelli 	/* Enable/disable 10BaseT auto and forced early DAC wake depending
12038dc84dcdSFlorian Fainelli 	 * on the negotiated speed, those settings should only be done
12048dc84dcdSFlorian Fainelli 	 * for 10Mbits/sec.
12058dc84dcdSFlorian Fainelli 	 */
12068dc84dcdSFlorian Fainelli 	if (phydev->speed == SPEED_10)
12078dc84dcdSFlorian Fainelli 		ret |= mask;
12088dc84dcdSFlorian Fainelli 	else
12098dc84dcdSFlorian Fainelli 		ret &= ~mask;
12108dc84dcdSFlorian Fainelli 	bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
12118dc84dcdSFlorian Fainelli }
12128dc84dcdSFlorian Fainelli 
lre_read_master_slave(struct phy_device * phydev)1213*03ab6c24SKamil Horák (2N) static int lre_read_master_slave(struct phy_device *phydev)
1214*03ab6c24SKamil Horák (2N) {
1215*03ab6c24SKamil Horák (2N) 	int cfg = MASTER_SLAVE_CFG_UNKNOWN, state;
1216*03ab6c24SKamil Horák (2N) 	int val;
1217*03ab6c24SKamil Horák (2N) 
1218*03ab6c24SKamil Horák (2N) 	/* In BroadR-Reach mode we are always capable of master-slave
1219*03ab6c24SKamil Horák (2N) 	 *  and there is no preferred master or slave configuration
1220*03ab6c24SKamil Horák (2N) 	 */
1221*03ab6c24SKamil Horák (2N) 	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
1222*03ab6c24SKamil Horák (2N) 	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
1223*03ab6c24SKamil Horák (2N) 
1224*03ab6c24SKamil Horák (2N) 	val = phy_read(phydev, MII_BCM54XX_LRECR);
1225*03ab6c24SKamil Horák (2N) 	if (val < 0)
1226*03ab6c24SKamil Horák (2N) 		return val;
1227*03ab6c24SKamil Horák (2N) 
1228*03ab6c24SKamil Horák (2N) 	if ((val & LRECR_LDSEN) == 0) {
1229*03ab6c24SKamil Horák (2N) 		if (val & LRECR_MASTER)
1230*03ab6c24SKamil Horák (2N) 			cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
1231*03ab6c24SKamil Horák (2N) 		else
1232*03ab6c24SKamil Horák (2N) 			cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
1233*03ab6c24SKamil Horák (2N) 	}
1234*03ab6c24SKamil Horák (2N) 
1235*03ab6c24SKamil Horák (2N) 	val = phy_read(phydev, MII_BCM54XX_LRELDSE);
1236*03ab6c24SKamil Horák (2N) 	if (val < 0)
1237*03ab6c24SKamil Horák (2N) 		return val;
1238*03ab6c24SKamil Horák (2N) 
1239*03ab6c24SKamil Horák (2N) 	if (val & LDSE_MASTER)
1240*03ab6c24SKamil Horák (2N) 		state = MASTER_SLAVE_STATE_MASTER;
1241*03ab6c24SKamil Horák (2N) 	else
1242*03ab6c24SKamil Horák (2N) 		state = MASTER_SLAVE_STATE_SLAVE;
1243*03ab6c24SKamil Horák (2N) 
1244*03ab6c24SKamil Horák (2N) 	phydev->master_slave_get = cfg;
1245*03ab6c24SKamil Horák (2N) 	phydev->master_slave_state = state;
1246*03ab6c24SKamil Horák (2N) 
1247*03ab6c24SKamil Horák (2N) 	return 0;
1248*03ab6c24SKamil Horák (2N) }
1249*03ab6c24SKamil Horák (2N) 
1250*03ab6c24SKamil Horák (2N) /* Read LDS Link Partner Ability in BroadR-Reach mode */
lre_read_lpa(struct phy_device * phydev)1251*03ab6c24SKamil Horák (2N) static int lre_read_lpa(struct phy_device *phydev)
1252*03ab6c24SKamil Horák (2N) {
1253*03ab6c24SKamil Horák (2N) 	int i, lrelpa;
1254*03ab6c24SKamil Horák (2N) 
1255*03ab6c24SKamil Horák (2N) 	if (phydev->autoneg != AUTONEG_ENABLE) {
1256*03ab6c24SKamil Horák (2N) 		if (!phydev->autoneg_complete) {
1257*03ab6c24SKamil Horák (2N) 			/* aneg not yet done, reset all relevant bits */
1258*03ab6c24SKamil Horák (2N) 			for (i = 0; i < ARRAY_SIZE(lds_br_bits); i++)
1259*03ab6c24SKamil Horák (2N) 				linkmode_clear_bit(lds_br_bits[i],
1260*03ab6c24SKamil Horák (2N) 						   phydev->lp_advertising);
1261*03ab6c24SKamil Horák (2N) 
1262*03ab6c24SKamil Horák (2N) 			return 0;
1263*03ab6c24SKamil Horák (2N) 		}
1264*03ab6c24SKamil Horák (2N) 
1265*03ab6c24SKamil Horák (2N) 		/* Long-Distance Signaling Link Partner Ability */
1266*03ab6c24SKamil Horák (2N) 		lrelpa = phy_read(phydev, MII_BCM54XX_LRELPA);
1267*03ab6c24SKamil Horák (2N) 		if (lrelpa < 0)
1268*03ab6c24SKamil Horák (2N) 			return lrelpa;
1269*03ab6c24SKamil Horák (2N) 
1270*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
1271*03ab6c24SKamil Horák (2N) 				 phydev->lp_advertising,
1272*03ab6c24SKamil Horák (2N) 				 lrelpa & LRELPA_PAUSE_ASYM);
1273*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
1274*03ab6c24SKamil Horák (2N) 				 phydev->lp_advertising,
1275*03ab6c24SKamil Horák (2N) 				 lrelpa & LRELPA_PAUSE);
1276*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
1277*03ab6c24SKamil Horák (2N) 				 phydev->lp_advertising,
1278*03ab6c24SKamil Horák (2N) 				 lrelpa & LRELPA_100_1PAIR);
1279*03ab6c24SKamil Horák (2N) 		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
1280*03ab6c24SKamil Horák (2N) 				 phydev->lp_advertising,
1281*03ab6c24SKamil Horák (2N) 				 lrelpa & LRELPA_10_1PAIR);
1282*03ab6c24SKamil Horák (2N) 	} else {
1283*03ab6c24SKamil Horák (2N) 		linkmode_zero(phydev->lp_advertising);
1284*03ab6c24SKamil Horák (2N) 	}
1285*03ab6c24SKamil Horák (2N) 
1286*03ab6c24SKamil Horák (2N) 	return 0;
1287*03ab6c24SKamil Horák (2N) }
1288*03ab6c24SKamil Horák (2N) 
lre_read_status_fixed(struct phy_device * phydev)1289*03ab6c24SKamil Horák (2N) static int lre_read_status_fixed(struct phy_device *phydev)
1290*03ab6c24SKamil Horák (2N) {
1291*03ab6c24SKamil Horák (2N) 	int lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
1292*03ab6c24SKamil Horák (2N) 
1293*03ab6c24SKamil Horák (2N) 	if (lrecr < 0)
1294*03ab6c24SKamil Horák (2N) 		return lrecr;
1295*03ab6c24SKamil Horák (2N) 
1296*03ab6c24SKamil Horák (2N) 	phydev->duplex = DUPLEX_FULL;
1297*03ab6c24SKamil Horák (2N) 
1298*03ab6c24SKamil Horák (2N) 	if (lrecr & LRECR_SPEED100)
1299*03ab6c24SKamil Horák (2N) 		phydev->speed = SPEED_100;
1300*03ab6c24SKamil Horák (2N) 	else
1301*03ab6c24SKamil Horák (2N) 		phydev->speed = SPEED_10;
1302*03ab6c24SKamil Horák (2N) 
1303*03ab6c24SKamil Horák (2N) 	return 0;
1304*03ab6c24SKamil Horák (2N) }
1305*03ab6c24SKamil Horák (2N) 
1306*03ab6c24SKamil Horák (2N) /**
1307*03ab6c24SKamil Horák (2N)  * lre_update_link - update link status in @phydev
1308*03ab6c24SKamil Horák (2N)  * @phydev: target phy_device struct
1309*03ab6c24SKamil Horák (2N)  * Return:  0 on success, < 0 on error
1310*03ab6c24SKamil Horák (2N)  *
1311*03ab6c24SKamil Horák (2N)  * Description: Update the value in phydev->link to reflect the
1312*03ab6c24SKamil Horák (2N)  *   current link value.  In order to do this, we need to read
1313*03ab6c24SKamil Horák (2N)  *   the status register twice, keeping the second value.
1314*03ab6c24SKamil Horák (2N)  *   This is a genphy_update_link modified to work on LRE registers
1315*03ab6c24SKamil Horák (2N)  *   of BroadR-Reach PHY
1316*03ab6c24SKamil Horák (2N)  */
lre_update_link(struct phy_device * phydev)1317*03ab6c24SKamil Horák (2N) static int lre_update_link(struct phy_device *phydev)
1318*03ab6c24SKamil Horák (2N) {
1319*03ab6c24SKamil Horák (2N) 	int status = 0, lrecr;
1320*03ab6c24SKamil Horák (2N) 
1321*03ab6c24SKamil Horák (2N) 	lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
1322*03ab6c24SKamil Horák (2N) 	if (lrecr < 0)
1323*03ab6c24SKamil Horák (2N) 		return lrecr;
1324*03ab6c24SKamil Horák (2N) 
1325*03ab6c24SKamil Horák (2N) 	/* Autoneg is being started, therefore disregard BMSR value and
1326*03ab6c24SKamil Horák (2N) 	 * report link as down.
1327*03ab6c24SKamil Horák (2N) 	 */
1328*03ab6c24SKamil Horák (2N) 	if (lrecr & BMCR_ANRESTART)
1329*03ab6c24SKamil Horák (2N) 		goto done;
1330*03ab6c24SKamil Horák (2N) 
1331*03ab6c24SKamil Horák (2N) 	/* The link state is latched low so that momentary link
1332*03ab6c24SKamil Horák (2N) 	 * drops can be detected. Do not double-read the status
1333*03ab6c24SKamil Horák (2N) 	 * in polling mode to detect such short link drops except
1334*03ab6c24SKamil Horák (2N) 	 * the link was already down.
1335*03ab6c24SKamil Horák (2N) 	 */
1336*03ab6c24SKamil Horák (2N) 	if (!phy_polling_mode(phydev) || !phydev->link) {
1337*03ab6c24SKamil Horák (2N) 		status = phy_read(phydev, MII_BCM54XX_LRESR);
1338*03ab6c24SKamil Horák (2N) 		if (status < 0)
1339*03ab6c24SKamil Horák (2N) 			return status;
1340*03ab6c24SKamil Horák (2N) 		else if (status & LRESR_LSTATUS)
1341*03ab6c24SKamil Horák (2N) 			goto done;
1342*03ab6c24SKamil Horák (2N) 	}
1343*03ab6c24SKamil Horák (2N) 
1344*03ab6c24SKamil Horák (2N) 	/* Read link and autonegotiation status */
1345*03ab6c24SKamil Horák (2N) 	status = phy_read(phydev, MII_BCM54XX_LRESR);
1346*03ab6c24SKamil Horák (2N) 	if (status < 0)
1347*03ab6c24SKamil Horák (2N) 		return status;
1348*03ab6c24SKamil Horák (2N) done:
1349*03ab6c24SKamil Horák (2N) 	phydev->link = status & LRESR_LSTATUS ? 1 : 0;
1350*03ab6c24SKamil Horák (2N) 	phydev->autoneg_complete = status & LRESR_LDSCOMPLETE ? 1 : 0;
1351*03ab6c24SKamil Horák (2N) 
1352*03ab6c24SKamil Horák (2N) 	/* Consider the case that autoneg was started and "aneg complete"
1353*03ab6c24SKamil Horák (2N) 	 * bit has been reset, but "link up" bit not yet.
1354*03ab6c24SKamil Horák (2N) 	 */
1355*03ab6c24SKamil Horák (2N) 	if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
1356*03ab6c24SKamil Horák (2N) 		phydev->link = 0;
1357*03ab6c24SKamil Horák (2N) 
1358*03ab6c24SKamil Horák (2N) 	return 0;
1359*03ab6c24SKamil Horák (2N) }
1360*03ab6c24SKamil Horák (2N) 
1361*03ab6c24SKamil Horák (2N) /* Get the status in BroadRReach mode just like genphy_read_status does
1362*03ab6c24SKamil Horák (2N) *   in normal mode
1363*03ab6c24SKamil Horák (2N) */
bcm54811_lre_read_status(struct phy_device * phydev)1364*03ab6c24SKamil Horák (2N) static int bcm54811_lre_read_status(struct phy_device *phydev)
1365*03ab6c24SKamil Horák (2N) {
1366*03ab6c24SKamil Horák (2N) 	int err, old_link = phydev->link;
1367*03ab6c24SKamil Horák (2N) 
1368*03ab6c24SKamil Horák (2N) 	/* Update the link, but return if there was an error */
1369*03ab6c24SKamil Horák (2N) 	err = lre_update_link(phydev);
1370*03ab6c24SKamil Horák (2N) 	if (err)
1371*03ab6c24SKamil Horák (2N) 		return err;
1372*03ab6c24SKamil Horák (2N) 
1373*03ab6c24SKamil Horák (2N) 	/* why bother the PHY if nothing can have changed */
1374*03ab6c24SKamil Horák (2N) 	if (phydev->autoneg ==
1375*03ab6c24SKamil Horák (2N) 		AUTONEG_ENABLE && old_link && phydev->link)
1376*03ab6c24SKamil Horák (2N) 		return 0;
1377*03ab6c24SKamil Horák (2N) 
1378*03ab6c24SKamil Horák (2N) 	phydev->speed = SPEED_UNKNOWN;
1379*03ab6c24SKamil Horák (2N) 	phydev->duplex = DUPLEX_UNKNOWN;
1380*03ab6c24SKamil Horák (2N) 	phydev->pause = 0;
1381*03ab6c24SKamil Horák (2N) 	phydev->asym_pause = 0;
1382*03ab6c24SKamil Horák (2N) 
1383*03ab6c24SKamil Horák (2N) 	err = lre_read_master_slave(phydev);
1384*03ab6c24SKamil Horák (2N) 	if (err < 0)
1385*03ab6c24SKamil Horák (2N) 		return err;
1386*03ab6c24SKamil Horák (2N) 
1387*03ab6c24SKamil Horák (2N) 	/* Read LDS Link Partner Ability */
1388*03ab6c24SKamil Horák (2N) 	err = lre_read_lpa(phydev);
1389*03ab6c24SKamil Horák (2N) 	if (err < 0)
1390*03ab6c24SKamil Horák (2N) 		return err;
1391*03ab6c24SKamil Horák (2N) 
1392*03ab6c24SKamil Horák (2N) 	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
1393*03ab6c24SKamil Horák (2N) 		phy_resolve_aneg_linkmode(phydev);
1394*03ab6c24SKamil Horák (2N) 	else if (phydev->autoneg == AUTONEG_DISABLE)
1395*03ab6c24SKamil Horák (2N) 		err = lre_read_status_fixed(phydev);
1396*03ab6c24SKamil Horák (2N) 
1397*03ab6c24SKamil Horák (2N) 	return err;
1398*03ab6c24SKamil Horák (2N) }
1399*03ab6c24SKamil Horák (2N) 
bcm54811_read_status(struct phy_device * phydev)1400*03ab6c24SKamil Horák (2N) static int bcm54811_read_status(struct phy_device *phydev)
1401*03ab6c24SKamil Horák (2N) {
1402*03ab6c24SKamil Horák (2N) 	struct bcm54xx_phy_priv *priv = phydev->priv;
1403*03ab6c24SKamil Horák (2N) 
1404*03ab6c24SKamil Horák (2N) 	if (priv->brr_mode)
1405*03ab6c24SKamil Horák (2N) 		return  bcm54811_lre_read_status(phydev);
1406*03ab6c24SKamil Horák (2N) 
1407*03ab6c24SKamil Horák (2N) 	return genphy_read_status(phydev);
1408*03ab6c24SKamil Horák (2N) }
1409*03ab6c24SKamil Horák (2N) 
1410d5bf9071SChristian Hohnstaedt static struct phy_driver broadcom_drivers[] = {
1411d5bf9071SChristian Hohnstaedt {
1412fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5411,
1413c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
1414c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5411",
1415dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
14165a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
14175a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
14185a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
14195a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1420c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
1421a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
14224567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
14238dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1424d5bf9071SChristian Hohnstaedt }, {
1425fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5421,
1426c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
1427c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5421",
1428dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
14295a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
14305a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
14315a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
14325a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1433c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
1434a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
14354567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
14368dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1437d5bf9071SChristian Hohnstaedt }, {
14380fc9ae10SRafał Miłecki 	.phy_id		= PHY_ID_BCM54210E,
14390fc9ae10SRafał Miłecki 	.phy_id_mask	= 0xfffffff0,
14400fc9ae10SRafał Miłecki 	.name		= "Broadcom BCM54210E",
1441dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
14428baddaa9SFlorian Fainelli 	.flags		= PHY_ALWAYS_CALL_SUSPEND,
14435a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
14445a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
14455a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
14465a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
14470fc9ae10SRafał Miłecki 	.config_init	= bcm54xx_config_init,
14480fc9ae10SRafał Miłecki 	.config_intr	= bcm_phy_config_intr,
14494567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
14508dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1451d6da08edSFlorian Fainelli 	.suspend	= bcm54xx_suspend,
1452d6da08edSFlorian Fainelli 	.resume		= bcm54xx_resume,
14538baddaa9SFlorian Fainelli 	.get_wol	= bcm54xx_phy_get_wol,
14548baddaa9SFlorian Fainelli 	.set_wol	= bcm54xx_phy_set_wol,
1455bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
14560fc9ae10SRafał Miłecki }, {
1457fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5461,
1458c4b41c9fSMaciej W. Rozycki 	.phy_id_mask	= 0xfffffff0,
1459c4b41c9fSMaciej W. Rozycki 	.name		= "Broadcom BCM5461",
1460dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
14615a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
14625a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
14635a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
14645a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1465c4b41c9fSMaciej W. Rozycki 	.config_init	= bcm54xx_config_init,
1466a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
14674567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
14688dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1469bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1470d5bf9071SChristian Hohnstaedt }, {
1471d92ead16SXo Wang 	.phy_id		= PHY_ID_BCM54612E,
1472d92ead16SXo Wang 	.phy_id_mask	= 0xfffffff0,
1473d92ead16SXo Wang 	.name		= "Broadcom BCM54612E",
1474dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
14755a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
14765a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
14775a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
14785a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1479d92ead16SXo Wang 	.config_init	= bcm54xx_config_init,
1480d92ead16SXo Wang 	.config_intr	= bcm_phy_config_intr,
14814567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
14828dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1483bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1484380b50aeSMarco von Rosenberg 	.suspend	= bcm54xx_suspend,
1485380b50aeSMarco von Rosenberg 	.resume		= bcm54xx_resume,
1486d92ead16SXo Wang }, {
14873bca4cf6SAlessio Igor Bogani 	.phy_id		= PHY_ID_BCM54616S,
14883bca4cf6SAlessio Igor Bogani 	.phy_id_mask	= 0xfffffff0,
14893bca4cf6SAlessio Igor Bogani 	.name		= "Broadcom BCM54616S",
1490dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
1491d15c7e87SRobert Hancock 	.soft_reset     = genphy_soft_reset,
14923bca4cf6SAlessio Igor Bogani 	.config_init	= bcm54xx_config_init,
1493042cb564STao Ren 	.config_aneg	= bcm54616s_config_aneg,
1494a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
14954567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
1496b9bcb953STao Ren 	.read_status	= bcm54616s_read_status,
1497b9bcb953STao Ren 	.probe		= bcm54616s_probe,
14988dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1499bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
15003bca4cf6SAlessio Igor Bogani }, {
1501fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5464,
1502b1394f96SPaul Gortmaker 	.phy_id_mask	= 0xfffffff0,
1503b1394f96SPaul Gortmaker 	.name		= "Broadcom BCM5464",
1504dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
15055a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15065a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15075a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15085a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1509b1394f96SPaul Gortmaker 	.config_init	= bcm54xx_config_init,
1510a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
15114567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
1512283da99aSVladimir Oltean 	.suspend	= genphy_suspend,
1513283da99aSVladimir Oltean 	.resume		= genphy_resume,
15148dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1515bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1516d5bf9071SChristian Hohnstaedt }, {
1517fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5481,
151857bb7e22SAnton Vorontsov 	.phy_id_mask	= 0xfffffff0,
151957bb7e22SAnton Vorontsov 	.name		= "Broadcom BCM5481",
1520dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
15215a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15225a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15235a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15245a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
152557bb7e22SAnton Vorontsov 	.config_init	= bcm54xx_config_init,
15269753c21fSHeiner Kallweit 	.config_aneg	= bcm5481_config_aneg,
1527a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
15284567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
15298dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1530bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1531d5bf9071SChristian Hohnstaedt }, {
1532b14995acSJon Mason 	.phy_id         = PHY_ID_BCM54810,
1533b14995acSJon Mason 	.phy_id_mask    = 0xfffffff0,
1534b14995acSJon Mason 	.name           = "Broadcom BCM54810",
1535dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
15365a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15375a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15385a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15395a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1540096516d0SJustin Chen 	.read_mmd	= bcm54810_read_mmd,
1541096516d0SJustin Chen 	.write_mmd	= bcm54810_write_mmd,
1542b14995acSJon Mason 	.config_init    = bcm54xx_config_init,
15439753c21fSHeiner Kallweit 	.config_aneg    = bcm5481_config_aneg,
1544b14995acSJon Mason 	.config_intr    = bcm_phy_config_intr,
15454567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
154672e78d22SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
1547fe26821fSFlorian Fainelli 	.resume		= bcm54xx_resume,
15488dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1549bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1550b14995acSJon Mason }, {
1551b0ed0bbfSKevin Lo 	.phy_id         = PHY_ID_BCM54811,
1552b0ed0bbfSKevin Lo 	.phy_id_mask    = 0xfffffff0,
1553b0ed0bbfSKevin Lo 	.name           = "Broadcom BCM54811",
1554b0ed0bbfSKevin Lo 	/* PHY_GBIT_FEATURES */
15555a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15565a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15575a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15585a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1559*03ab6c24SKamil Horák (2N) 	.config_init    = bcm54xx_config_init,
1560*03ab6c24SKamil Horák (2N) 	.config_aneg    = bcm54811_config_aneg,
1561b0ed0bbfSKevin Lo 	.config_intr    = bcm_phy_config_intr,
15624567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
1563*03ab6c24SKamil Horák (2N) 	.read_status	= bcm54811_read_status,
1564*03ab6c24SKamil Horák (2N) 	.get_features	= bcm5481x_read_abilities,
156572e78d22SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
1566b0ed0bbfSKevin Lo 	.resume		= bcm54xx_resume,
15678dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1568bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1569b0ed0bbfSKevin Lo }, {
1570fcb26ec5SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5482,
157103157ac3SNate Case 	.phy_id_mask	= 0xfffffff0,
157203157ac3SNate Case 	.name		= "Broadcom BCM5482",
1573dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
15745a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15755a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15765a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15775a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
15781e2e61afSMichael Walle 	.config_init	= bcm54xx_config_init,
1579a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
15804567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
15818dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1582bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1583d5bf9071SChristian Hohnstaedt }, {
1584772638b6SMatt Carlson 	.phy_id		= PHY_ID_BCM50610,
1585772638b6SMatt Carlson 	.phy_id_mask	= 0xfffffff0,
1586772638b6SMatt Carlson 	.name		= "Broadcom BCM50610",
1587dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
15885a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
15895a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
15905a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
15915a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1592772638b6SMatt Carlson 	.config_init	= bcm54xx_config_init,
1593a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
15944567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
15958dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
159638b6a907SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
159738b6a907SFlorian Fainelli 	.resume		= bcm54xx_resume,
1598bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1599d5bf9071SChristian Hohnstaedt }, {
16004f4598fdSMatt Carlson 	.phy_id		= PHY_ID_BCM50610M,
16014f4598fdSMatt Carlson 	.phy_id_mask	= 0xfffffff0,
16024f4598fdSMatt Carlson 	.name		= "Broadcom BCM50610M",
1603dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
16045a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
16055a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
16065a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
16075a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
16084f4598fdSMatt Carlson 	.config_init	= bcm54xx_config_init,
1609a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
16104567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
16118dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
161238b6a907SFlorian Fainelli 	.suspend	= bcm54xx_suspend,
161338b6a907SFlorian Fainelli 	.resume		= bcm54xx_resume,
1614bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1615d5bf9071SChristian Hohnstaedt }, {
1616d9221e66SMatt Carlson 	.phy_id		= PHY_ID_BCM57780,
16172fbb69aaSMatt Carlson 	.phy_id_mask	= 0xfffffff0,
16182fbb69aaSMatt Carlson 	.name		= "Broadcom BCM57780",
1619dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
16205a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
16215a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
16225a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
16235a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
16242fbb69aaSMatt Carlson 	.config_init	= bcm54xx_config_init,
1625a1cba561SArun Parameswaran 	.config_intr	= bcm_phy_config_intr,
16264567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
16278dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1628bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1629d5bf9071SChristian Hohnstaedt }, {
16306a443a0fSMatt Carlson 	.phy_id		= PHY_ID_BCMAC131,
1631d7a2ed92SMatt Carlson 	.phy_id_mask	= 0xfffffff0,
1632d7a2ed92SMatt Carlson 	.name		= "Broadcom BCMAC131",
1633dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
1634d7a2ed92SMatt Carlson 	.config_init	= brcm_fet_config_init,
1635d7a2ed92SMatt Carlson 	.config_intr	= brcm_fet_config_intr,
16364567d5c3SIoana Ciornei 	.handle_interrupt = brcm_fet_handle_interrupt,
16370630f64dSFlorian Fainelli 	.suspend	= brcm_fet_suspend,
16380630f64dSFlorian Fainelli 	.resume		= brcm_fet_config_init,
1639d5bf9071SChristian Hohnstaedt }, {
16407a938f80SDmitry Baryshkov 	.phy_id		= PHY_ID_BCM5241,
16417a938f80SDmitry Baryshkov 	.phy_id_mask	= 0xfffffff0,
16427a938f80SDmitry Baryshkov 	.name		= "Broadcom BCM5241",
1643dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
16447a938f80SDmitry Baryshkov 	.config_init	= brcm_fet_config_init,
16457a938f80SDmitry Baryshkov 	.config_intr	= brcm_fet_config_intr,
16464567d5c3SIoana Ciornei 	.handle_interrupt = brcm_fet_handle_interrupt,
16470630f64dSFlorian Fainelli 	.suspend	= brcm_fet_suspend,
16480630f64dSFlorian Fainelli 	.resume		= brcm_fet_config_init,
164928dc4c8fSFlorian Fainelli }, {
16503abbd069SGiulio Benetti 	.phy_id		= PHY_ID_BCM5221,
16513abbd069SGiulio Benetti 	.phy_id_mask	= 0xfffffff0,
16523abbd069SGiulio Benetti 	.name		= "Broadcom BCM5221",
16533abbd069SGiulio Benetti 	/* PHY_BASIC_FEATURES */
16543abbd069SGiulio Benetti 	.config_init	= brcm_fet_config_init,
16553abbd069SGiulio Benetti 	.config_intr	= brcm_fet_config_intr,
16563abbd069SGiulio Benetti 	.handle_interrupt = brcm_fet_handle_interrupt,
16573abbd069SGiulio Benetti 	.suspend	= brcm_fet_suspend,
16583abbd069SGiulio Benetti 	.resume		= brcm_fet_config_init,
16593abbd069SGiulio Benetti 	.config_aneg	= bcm5221_config_aneg,
16603abbd069SGiulio Benetti 	.read_status	= bcm5221_read_status,
16613abbd069SGiulio Benetti }, {
166228dc4c8fSFlorian Fainelli 	.phy_id		= PHY_ID_BCM5395,
166328dc4c8fSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
166428dc4c8fSFlorian Fainelli 	.name		= "Broadcom BCM5395",
166528dc4c8fSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
1666dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
166728dc4c8fSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
166828dc4c8fSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
16695a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
16705a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
16718dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1672bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
167323b83922SBhadram Varka }, {
1674123aff2aSFlorian Fainelli 	.phy_id		= PHY_ID_BCM53125,
1675123aff2aSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
1676123aff2aSFlorian Fainelli 	.name		= "Broadcom BCM53125",
1677123aff2aSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
1678123aff2aSFlorian Fainelli 	/* PHY_GBIT_FEATURES */
1679123aff2aSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
1680123aff2aSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
16815a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
16825a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
1683123aff2aSFlorian Fainelli 	.config_init	= bcm54xx_config_init,
1684123aff2aSFlorian Fainelli 	.config_intr	= bcm_phy_config_intr,
16854567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
16868dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1687bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
1688123aff2aSFlorian Fainelli }, {
168939bfb3c1SKurt Kanzenbach 	.phy_id		= PHY_ID_BCM53128,
169039bfb3c1SKurt Kanzenbach 	.phy_id_mask	= 0xfffffff0,
169139bfb3c1SKurt Kanzenbach 	.name		= "Broadcom BCM53128",
169239bfb3c1SKurt Kanzenbach 	.flags		= PHY_IS_INTERNAL,
169339bfb3c1SKurt Kanzenbach 	/* PHY_GBIT_FEATURES */
169439bfb3c1SKurt Kanzenbach 	.get_sset_count	= bcm_phy_get_sset_count,
169539bfb3c1SKurt Kanzenbach 	.get_strings	= bcm_phy_get_strings,
169639bfb3c1SKurt Kanzenbach 	.get_stats	= bcm54xx_get_stats,
169739bfb3c1SKurt Kanzenbach 	.probe		= bcm54xx_phy_probe,
169839bfb3c1SKurt Kanzenbach 	.config_init	= bcm54xx_config_init,
169939bfb3c1SKurt Kanzenbach 	.config_intr	= bcm_phy_config_intr,
170039bfb3c1SKurt Kanzenbach 	.handle_interrupt = bcm_phy_handle_interrupt,
170139bfb3c1SKurt Kanzenbach 	.link_change_notify	= bcm54xx_link_change_notify,
1702bd5736e1SFlorian Fainelli 	.led_brightness_set	= bcm_phy_led_brightness_set,
170339bfb3c1SKurt Kanzenbach }, {
170423b83922SBhadram Varka 	.phy_id         = PHY_ID_BCM89610,
170523b83922SBhadram Varka 	.phy_id_mask    = 0xfffffff0,
170623b83922SBhadram Varka 	.name           = "Broadcom BCM89610",
1707dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
17085a32fcdbSFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,
17095a32fcdbSFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,
17105a32fcdbSFlorian Fainelli 	.get_stats	= bcm54xx_get_stats,
17115a32fcdbSFlorian Fainelli 	.probe		= bcm54xx_phy_probe,
171223b83922SBhadram Varka 	.config_init    = bcm54xx_config_init,
171323b83922SBhadram Varka 	.config_intr    = bcm_phy_config_intr,
17144567d5c3SIoana Ciornei 	.handle_interrupt = bcm_phy_handle_interrupt,
17158dc84dcdSFlorian Fainelli 	.link_change_notify	= bcm54xx_link_change_notify,
1716d5bf9071SChristian Hohnstaedt } };
17177a938f80SDmitry Baryshkov 
171850fd7150SJohan Hovold module_phy_driver(broadcom_drivers);
17194e4f10f6SDavid Woodhouse 
1720cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
1721fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5411, 0xfffffff0 },
1722fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5421, 0xfffffff0 },
17230fc9ae10SRafał Miłecki 	{ PHY_ID_BCM54210E, 0xfffffff0 },
1724fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5461, 0xfffffff0 },
1725d92ead16SXo Wang 	{ PHY_ID_BCM54612E, 0xfffffff0 },
17263bca4cf6SAlessio Igor Bogani 	{ PHY_ID_BCM54616S, 0xfffffff0 },
1727fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5464, 0xfffffff0 },
17283c25a860SAaro Koskinen 	{ PHY_ID_BCM5481, 0xfffffff0 },
1729b14995acSJon Mason 	{ PHY_ID_BCM54810, 0xfffffff0 },
1730b0ed0bbfSKevin Lo 	{ PHY_ID_BCM54811, 0xfffffff0 },
1731fcb26ec5SDmitry Baryshkov 	{ PHY_ID_BCM5482, 0xfffffff0 },
17324e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM50610, 0xfffffff0 },
17334e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM50610M, 0xfffffff0 },
17344e4f10f6SDavid Woodhouse 	{ PHY_ID_BCM57780, 0xfffffff0 },
17354e4f10f6SDavid Woodhouse 	{ PHY_ID_BCMAC131, 0xfffffff0 },
17363abbd069SGiulio Benetti 	{ PHY_ID_BCM5221, 0xfffffff0 },
17377a938f80SDmitry Baryshkov 	{ PHY_ID_BCM5241, 0xfffffff0 },
173828dc4c8fSFlorian Fainelli 	{ PHY_ID_BCM5395, 0xfffffff0 },
1739123aff2aSFlorian Fainelli 	{ PHY_ID_BCM53125, 0xfffffff0 },
174039bfb3c1SKurt Kanzenbach 	{ PHY_ID_BCM53128, 0xfffffff0 },
174123b83922SBhadram Varka 	{ PHY_ID_BCM89610, 0xfffffff0 },
17424e4f10f6SDavid Woodhouse 	{ }
17434e4f10f6SDavid Woodhouse };
17444e4f10f6SDavid Woodhouse 
17454e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
1746