162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	drivers/net/phy/broadcom.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
662306a36Sopenharmony_ci *	transceivers.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *	Copyright (c) 2006  Maciej W. Rozycki
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Inspired by code written by Amy Fong.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "bcm-phy-lib.h"
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/phy.h>
1762306a36Sopenharmony_ci#include <linux/pm_wakeup.h>
1862306a36Sopenharmony_ci#include <linux/brcmphy.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/irq.h>
2262306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define BRCM_PHY_MODEL(phydev) \
2562306a36Sopenharmony_ci	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define BRCM_PHY_REV(phydev) \
2862306a36Sopenharmony_ci	((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom PHY driver");
3162306a36Sopenharmony_ciMODULE_AUTHOR("Maciej W. Rozycki");
3262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct bcm54xx_phy_priv {
3562306a36Sopenharmony_ci	u64	*stats;
3662306a36Sopenharmony_ci	struct bcm_ptp_private *ptp;
3762306a36Sopenharmony_ci	int	wake_irq;
3862306a36Sopenharmony_ci	bool	wake_irq_enabled;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic bool bcm54xx_phy_can_wakeup(struct phy_device *phydev)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv = phydev->priv;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int bcm54xx_config_clock_delay(struct phy_device *phydev)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int rc, val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* handling PHY's internal RX clock delay */
5362306a36Sopenharmony_ci	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
5462306a36Sopenharmony_ci	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
5562306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
5662306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
5762306a36Sopenharmony_ci		/* Disable RGMII RXC-RXD skew */
5862306a36Sopenharmony_ci		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
6162306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
6262306a36Sopenharmony_ci		/* Enable RGMII RXC-RXD skew */
6362306a36Sopenharmony_ci		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
6662306a36Sopenharmony_ci				  val);
6762306a36Sopenharmony_ci	if (rc < 0)
6862306a36Sopenharmony_ci		return rc;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* handling PHY's internal TX clock delay */
7162306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
7262306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
7362306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
7462306a36Sopenharmony_ci		/* Disable internal TX clock delay */
7562306a36Sopenharmony_ci		val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
7862306a36Sopenharmony_ci	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
7962306a36Sopenharmony_ci		/* Enable internal TX clock delay */
8062306a36Sopenharmony_ci		val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
8362306a36Sopenharmony_ci	if (rc < 0)
8462306a36Sopenharmony_ci		return rc;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int bcm54210e_config_init(struct phy_device *phydev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int val;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	bcm54xx_config_clock_delay(phydev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
9662306a36Sopenharmony_ci		val = phy_read(phydev, MII_CTRL1000);
9762306a36Sopenharmony_ci		val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
9862306a36Sopenharmony_ci		phy_write(phydev, MII_CTRL1000, val);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int bcm54612e_config_init(struct phy_device *phydev)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int reg;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	bcm54xx_config_clock_delay(phydev);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
11162306a36Sopenharmony_ci	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
11262306a36Sopenharmony_ci		int err;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
11562306a36Sopenharmony_ci		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
11662306a36Sopenharmony_ci					BCM54612E_LED4_CLK125OUT_EN | reg);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		if (err < 0)
11962306a36Sopenharmony_ci			return err;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int bcm54616s_config_init(struct phy_device *phydev)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int rc, val;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
13062306a36Sopenharmony_ci	    phydev->interface != PHY_INTERFACE_MODE_1000BASEX)
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Ensure proper interface mode is selected. */
13462306a36Sopenharmony_ci	/* Disable RGMII mode */
13562306a36Sopenharmony_ci	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
13662306a36Sopenharmony_ci	if (val < 0)
13762306a36Sopenharmony_ci		return val;
13862306a36Sopenharmony_ci	val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN;
13962306a36Sopenharmony_ci	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
14062306a36Sopenharmony_ci	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
14162306a36Sopenharmony_ci				  val);
14262306a36Sopenharmony_ci	if (rc < 0)
14362306a36Sopenharmony_ci		return rc;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Select 1000BASE-X register set (primary SerDes) */
14662306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
14762306a36Sopenharmony_ci	if (val < 0)
14862306a36Sopenharmony_ci		return val;
14962306a36Sopenharmony_ci	val |= BCM54XX_SHD_MODE_1000BX;
15062306a36Sopenharmony_ci	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
15162306a36Sopenharmony_ci	if (rc < 0)
15262306a36Sopenharmony_ci		return rc;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Power down SerDes interface */
15562306a36Sopenharmony_ci	rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
15662306a36Sopenharmony_ci	if (rc < 0)
15762306a36Sopenharmony_ci		return rc;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Select proper interface mode */
16062306a36Sopenharmony_ci	val &= ~BCM54XX_SHD_INTF_SEL_MASK;
16162306a36Sopenharmony_ci	val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ?
16262306a36Sopenharmony_ci		BCM54XX_SHD_INTF_SEL_SGMII :
16362306a36Sopenharmony_ci		BCM54XX_SHD_INTF_SEL_GBIC;
16462306a36Sopenharmony_ci	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
16562306a36Sopenharmony_ci	if (rc < 0)
16662306a36Sopenharmony_ci		return rc;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Power up SerDes interface */
16962306a36Sopenharmony_ci	rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
17062306a36Sopenharmony_ci	if (rc < 0)
17162306a36Sopenharmony_ci		return rc;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Select copper register set */
17462306a36Sopenharmony_ci	val &= ~BCM54XX_SHD_MODE_1000BX;
17562306a36Sopenharmony_ci	rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
17662306a36Sopenharmony_ci	if (rc < 0)
17762306a36Sopenharmony_ci		return rc;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Power up copper interface */
18062306a36Sopenharmony_ci	return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
18462306a36Sopenharmony_cistatic int bcm50610_a0_workaround(struct phy_device *phydev)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int err;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
18962306a36Sopenharmony_ci				MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
19062306a36Sopenharmony_ci				MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
19162306a36Sopenharmony_ci	if (err < 0)
19262306a36Sopenharmony_ci		return err;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
19562306a36Sopenharmony_ci				MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
19662306a36Sopenharmony_ci	if (err < 0)
19762306a36Sopenharmony_ci		return err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
20062306a36Sopenharmony_ci				MII_BCM54XX_EXP_EXP75_VDACCTRL);
20162306a36Sopenharmony_ci	if (err < 0)
20262306a36Sopenharmony_ci		return err;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
20562306a36Sopenharmony_ci				MII_BCM54XX_EXP_EXP96_MYST);
20662306a36Sopenharmony_ci	if (err < 0)
20762306a36Sopenharmony_ci		return err;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
21062306a36Sopenharmony_ci				MII_BCM54XX_EXP_EXP97_MYST);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return err;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int bcm54xx_phydsp_config(struct phy_device *phydev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int err, err2;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Enable the SMDSP clock */
22062306a36Sopenharmony_ci	err = bcm54xx_auxctl_write(phydev,
22162306a36Sopenharmony_ci				   MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
22262306a36Sopenharmony_ci				   MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
22362306a36Sopenharmony_ci				   MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
22462306a36Sopenharmony_ci	if (err < 0)
22562306a36Sopenharmony_ci		return err;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
22862306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
22962306a36Sopenharmony_ci		/* Clear bit 9 to fix a phy interop issue. */
23062306a36Sopenharmony_ci		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
23162306a36Sopenharmony_ci					MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
23262306a36Sopenharmony_ci		if (err < 0)
23362306a36Sopenharmony_ci			goto error;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		if (phydev->drv->phy_id == PHY_ID_BCM50610) {
23662306a36Sopenharmony_ci			err = bcm50610_a0_workaround(phydev);
23762306a36Sopenharmony_ci			if (err < 0)
23862306a36Sopenharmony_ci				goto error;
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
24362306a36Sopenharmony_ci		int val;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
24662306a36Sopenharmony_ci		if (val < 0)
24762306a36Sopenharmony_ci			goto error;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
25062306a36Sopenharmony_ci		err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cierror:
25462306a36Sopenharmony_ci	/* Disable the SMDSP clock */
25562306a36Sopenharmony_ci	err2 = bcm54xx_auxctl_write(phydev,
25662306a36Sopenharmony_ci				    MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
25762306a36Sopenharmony_ci				    MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Return the first error reported. */
26062306a36Sopenharmony_ci	return err ? err : err2;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	u32 orig;
26662306a36Sopenharmony_ci	int val;
26762306a36Sopenharmony_ci	bool clk125en = true;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Abort if we are using an untested phy. */
27062306a36Sopenharmony_ci	if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
27162306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
27262306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
27362306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E &&
27462306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
27562306a36Sopenharmony_ci	    BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
27662306a36Sopenharmony_ci		return;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
27962306a36Sopenharmony_ci	if (val < 0)
28062306a36Sopenharmony_ci		return;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	orig = val;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
28562306a36Sopenharmony_ci	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
28662306a36Sopenharmony_ci	    BRCM_PHY_REV(phydev) >= 0x3) {
28762306a36Sopenharmony_ci		/*
28862306a36Sopenharmony_ci		 * Here, bit 0 _disables_ CLK125 when set.
28962306a36Sopenharmony_ci		 * This bit is set by default.
29062306a36Sopenharmony_ci		 */
29162306a36Sopenharmony_ci		clk125en = false;
29262306a36Sopenharmony_ci	} else {
29362306a36Sopenharmony_ci		if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
29462306a36Sopenharmony_ci			if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
29562306a36Sopenharmony_ci				/* Here, bit 0 _enables_ CLK125 when set */
29662306a36Sopenharmony_ci				val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
29762306a36Sopenharmony_ci			}
29862306a36Sopenharmony_ci			clk125en = false;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
30362306a36Sopenharmony_ci		val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
30462306a36Sopenharmony_ci	else
30562306a36Sopenharmony_ci		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
30862306a36Sopenharmony_ci		if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E ||
30962306a36Sopenharmony_ci		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
31062306a36Sopenharmony_ci		    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
31162306a36Sopenharmony_ci			val |= BCM54XX_SHD_SCR3_RXCTXC_DIS;
31262306a36Sopenharmony_ci		else
31362306a36Sopenharmony_ci			val |= BCM54XX_SHD_SCR3_TRDDAPD;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (orig != val)
31762306a36Sopenharmony_ci		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
32062306a36Sopenharmony_ci	if (val < 0)
32162306a36Sopenharmony_ci		return;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	orig = val;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
32662306a36Sopenharmony_ci		val |= BCM54XX_SHD_APD_EN;
32762306a36Sopenharmony_ci	else
32862306a36Sopenharmony_ci		val &= ~BCM54XX_SHD_APD_EN;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (orig != val)
33162306a36Sopenharmony_ci		bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void bcm54xx_ptp_stop(struct phy_device *phydev)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv = phydev->priv;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (priv->ptp)
33962306a36Sopenharmony_ci		bcm_ptp_stop(priv->ptp);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void bcm54xx_ptp_config_init(struct phy_device *phydev)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv = phydev->priv;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (priv->ptp)
34762306a36Sopenharmony_ci		bcm_ptp_config_init(phydev);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int bcm54xx_config_init(struct phy_device *phydev)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int reg, err, val;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BCM54XX_ECR);
35562306a36Sopenharmony_ci	if (reg < 0)
35662306a36Sopenharmony_ci		return reg;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Mask interrupts globally.  */
35962306a36Sopenharmony_ci	reg |= MII_BCM54XX_ECR_IM;
36062306a36Sopenharmony_ci	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
36162306a36Sopenharmony_ci	if (err < 0)
36262306a36Sopenharmony_ci		return err;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Unmask events we are interested in.  */
36562306a36Sopenharmony_ci	reg = ~(MII_BCM54XX_INT_DUPLEX |
36662306a36Sopenharmony_ci		MII_BCM54XX_INT_SPEED |
36762306a36Sopenharmony_ci		MII_BCM54XX_INT_LINK);
36862306a36Sopenharmony_ci	err = phy_write(phydev, MII_BCM54XX_IMR, reg);
36962306a36Sopenharmony_ci	if (err < 0)
37062306a36Sopenharmony_ci		return err;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
37362306a36Sopenharmony_ci	     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
37462306a36Sopenharmony_ci	    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
37562306a36Sopenharmony_ci		bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	bcm54xx_adjust_rxrefclk(phydev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	switch (BRCM_PHY_MODEL(phydev)) {
38062306a36Sopenharmony_ci	case PHY_ID_BCM50610:
38162306a36Sopenharmony_ci	case PHY_ID_BCM50610M:
38262306a36Sopenharmony_ci		err = bcm54xx_config_clock_delay(phydev);
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	case PHY_ID_BCM54210E:
38562306a36Sopenharmony_ci		err = bcm54210e_config_init(phydev);
38662306a36Sopenharmony_ci		break;
38762306a36Sopenharmony_ci	case PHY_ID_BCM54612E:
38862306a36Sopenharmony_ci		err = bcm54612e_config_init(phydev);
38962306a36Sopenharmony_ci		break;
39062306a36Sopenharmony_ci	case PHY_ID_BCM54616S:
39162306a36Sopenharmony_ci		err = bcm54616s_config_init(phydev);
39262306a36Sopenharmony_ci		break;
39362306a36Sopenharmony_ci	case PHY_ID_BCM54810:
39462306a36Sopenharmony_ci		/* For BCM54810, we need to disable BroadR-Reach function */
39562306a36Sopenharmony_ci		val = bcm_phy_read_exp(phydev,
39662306a36Sopenharmony_ci				       BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
39762306a36Sopenharmony_ci		val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
39862306a36Sopenharmony_ci		err = bcm_phy_write_exp(phydev,
39962306a36Sopenharmony_ci					BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
40062306a36Sopenharmony_ci					val);
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci	if (err)
40462306a36Sopenharmony_ci		return err;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	bcm54xx_phydsp_config(phydev);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* For non-SFP setups, encode link speed into LED1 and LED3 pair
40962306a36Sopenharmony_ci	 * (green/amber).
41062306a36Sopenharmony_ci	 * Also flash these two LEDs on activity. This means configuring
41162306a36Sopenharmony_ci	 * them for MULTICOLOR and encoding link/activity into them.
41262306a36Sopenharmony_ci	 * Don't do this for devices on an SFP module, since some of these
41362306a36Sopenharmony_ci	 * use the LED outputs to control the SFP LOS signal, and changing
41462306a36Sopenharmony_ci	 * these settings will cause LOS to malfunction.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	if (!phy_on_sfp(phydev)) {
41762306a36Sopenharmony_ci		val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
41862306a36Sopenharmony_ci			BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
41962306a36Sopenharmony_ci		bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		val = BCM_LED_MULTICOLOR_IN_PHASE |
42262306a36Sopenharmony_ci			BCM54XX_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
42362306a36Sopenharmony_ci			BCM54XX_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
42462306a36Sopenharmony_ci		bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	bcm54xx_ptp_config_init(phydev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Acknowledge any left over interrupt and charge the device for
43062306a36Sopenharmony_ci	 * wake-up.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
43362306a36Sopenharmony_ci	if (err < 0)
43462306a36Sopenharmony_ci		return err;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (err)
43762306a36Sopenharmony_ci		pm_wakeup_event(&phydev->mdio.dev, 0);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	int ret = 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND))
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL);
45062306a36Sopenharmony_ci	if (ret < 0)
45162306a36Sopenharmony_ci		goto out;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (enable)
45462306a36Sopenharmony_ci		ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP;
45562306a36Sopenharmony_ci	else
45662306a36Sopenharmony_ci		ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, ret);
45962306a36Sopenharmony_ciout:
46062306a36Sopenharmony_ci	return ret;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv = phydev->priv;
46662306a36Sopenharmony_ci	int ret = 0;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (!bcm54xx_phy_can_wakeup(phydev))
46962306a36Sopenharmony_ci		return ret;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (priv->wake_irq_enabled != state) {
47262306a36Sopenharmony_ci		if (state)
47362306a36Sopenharmony_ci			ret = enable_irq_wake(priv->wake_irq);
47462306a36Sopenharmony_ci		else
47562306a36Sopenharmony_ci			ret = disable_irq_wake(priv->wake_irq);
47662306a36Sopenharmony_ci		priv->wake_irq_enabled = state;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return ret;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int bcm54xx_suspend(struct phy_device *phydev)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	int ret = 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	bcm54xx_ptp_stop(phydev);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
48962306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
49062306a36Sopenharmony_ci	if (ret < 0)
49162306a36Sopenharmony_ci		return ret;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (phydev->wol_enabled)
49462306a36Sopenharmony_ci		return bcm54xx_set_wakeup_irq(phydev, true);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* We cannot use a read/modify/write here otherwise the PHY gets into
49762306a36Sopenharmony_ci	 * a bad state where its LEDs keep flashing, thus defeating the purpose
49862306a36Sopenharmony_ci	 * of low power mode.
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
50162306a36Sopenharmony_ci	if (ret < 0)
50262306a36Sopenharmony_ci		return ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	return bcm54xx_iddq_set(phydev, true);
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic int bcm54xx_resume(struct phy_device *phydev)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	int ret = 0;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (phydev->wol_enabled) {
51262306a36Sopenharmony_ci		ret = bcm54xx_set_wakeup_irq(phydev, false);
51362306a36Sopenharmony_ci		if (ret)
51462306a36Sopenharmony_ci			return ret;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ret = bcm54xx_iddq_set(phydev, false);
51862306a36Sopenharmony_ci	if (ret < 0)
51962306a36Sopenharmony_ci		return ret;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* Writes to register other than BMCR would be ignored
52262306a36Sopenharmony_ci	 * unless we clear the PDOWN bit first
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	ret = genphy_resume(phydev);
52562306a36Sopenharmony_ci	if (ret < 0)
52662306a36Sopenharmony_ci		return ret;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* Upon exiting power down, the PHY remains in an internal reset state
52962306a36Sopenharmony_ci	 * for 40us
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	fsleep(40);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* Issue a soft reset after clearing the power down bit
53462306a36Sopenharmony_ci	 * and before doing any other configuration.
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) {
53762306a36Sopenharmony_ci		ret = genphy_soft_reset(phydev);
53862306a36Sopenharmony_ci		if (ret < 0)
53962306a36Sopenharmony_ci			return ret;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return bcm54xx_config_init(phydev);
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic int bcm54810_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	return -EOPNOTSUPP;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
55162306a36Sopenharmony_ci			      u16 val)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	return -EOPNOTSUPP;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic int bcm54811_config_init(struct phy_device *phydev)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	int err, reg;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Disable BroadR-Reach function. */
56162306a36Sopenharmony_ci	reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
56262306a36Sopenharmony_ci	reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
56362306a36Sopenharmony_ci	err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
56462306a36Sopenharmony_ci				reg);
56562306a36Sopenharmony_ci	if (err < 0)
56662306a36Sopenharmony_ci		return err;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	err = bcm54xx_config_init(phydev);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
57162306a36Sopenharmony_ci	if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
57262306a36Sopenharmony_ci		reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
57362306a36Sopenharmony_ci		err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
57462306a36Sopenharmony_ci					BCM54612E_LED4_CLK125OUT_EN | reg);
57562306a36Sopenharmony_ci		if (err < 0)
57662306a36Sopenharmony_ci			return err;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return err;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int bcm5481_config_aneg(struct phy_device *phydev)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct device_node *np = phydev->mdio.dev.of_node;
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* Aneg firstly. */
58862306a36Sopenharmony_ci	ret = genphy_config_aneg(phydev);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* Then we can set up the delay. */
59162306a36Sopenharmony_ci	bcm54xx_config_clock_delay(phydev);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (of_property_read_bool(np, "enet-phy-lane-swap")) {
59462306a36Sopenharmony_ci		/* Lane Swap - Undocumented register...magic! */
59562306a36Sopenharmony_ci		ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
59662306a36Sopenharmony_ci					0x11B);
59762306a36Sopenharmony_ci		if (ret < 0)
59862306a36Sopenharmony_ci			return ret;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return ret;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistruct bcm54616s_phy_priv {
60562306a36Sopenharmony_ci	bool mode_1000bx_en;
60662306a36Sopenharmony_ci};
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic int bcm54616s_probe(struct phy_device *phydev)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct bcm54616s_phy_priv *priv;
61162306a36Sopenharmony_ci	int val;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
61462306a36Sopenharmony_ci	if (!priv)
61562306a36Sopenharmony_ci		return -ENOMEM;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	phydev->priv = priv;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
62062306a36Sopenharmony_ci	if (val < 0)
62162306a36Sopenharmony_ci		return val;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0]
62462306a36Sopenharmony_ci	 * is 01b, and the link between PHY and its link partner can be
62562306a36Sopenharmony_ci	 * either 1000Base-X or 100Base-FX.
62662306a36Sopenharmony_ci	 * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
62762306a36Sopenharmony_ci	 * support is still missing as of now.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
63062306a36Sopenharmony_ci		val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL);
63162306a36Sopenharmony_ci		if (val < 0)
63262306a36Sopenharmony_ci			return val;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		/* Bit 0 of the SerDes 100-FX Control register, when set
63562306a36Sopenharmony_ci		 * to 1, sets the MII/RGMII -> 100BASE-FX configuration.
63662306a36Sopenharmony_ci		 * When this bit is set to 0, it sets the GMII/RGMII ->
63762306a36Sopenharmony_ci		 * 1000BASE-X configuration.
63862306a36Sopenharmony_ci		 */
63962306a36Sopenharmony_ci		if (!(val & BCM54616S_100FX_MODE))
64062306a36Sopenharmony_ci			priv->mode_1000bx_en = true;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		phydev->port = PORT_FIBRE;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	return 0;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int bcm54616s_config_aneg(struct phy_device *phydev)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct bcm54616s_phy_priv *priv = phydev->priv;
65162306a36Sopenharmony_ci	int ret;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* Aneg firstly. */
65462306a36Sopenharmony_ci	if (priv->mode_1000bx_en)
65562306a36Sopenharmony_ci		ret = genphy_c37_config_aneg(phydev);
65662306a36Sopenharmony_ci	else
65762306a36Sopenharmony_ci		ret = genphy_config_aneg(phydev);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Then we can set up the delay. */
66062306a36Sopenharmony_ci	bcm54xx_config_clock_delay(phydev);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return ret;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic int bcm54616s_read_status(struct phy_device *phydev)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct bcm54616s_phy_priv *priv = phydev->priv;
66862306a36Sopenharmony_ci	int err;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (priv->mode_1000bx_en)
67162306a36Sopenharmony_ci		err = genphy_c37_read_status(phydev);
67262306a36Sopenharmony_ci	else
67362306a36Sopenharmony_ci		err = genphy_read_status(phydev);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	return err;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic int brcm_fet_config_init(struct phy_device *phydev)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	int reg, err, err2, brcmtest;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* Reset the PHY to bring it to a known state. */
68362306a36Sopenharmony_ci	err = phy_write(phydev, MII_BMCR, BMCR_RESET);
68462306a36Sopenharmony_ci	if (err < 0)
68562306a36Sopenharmony_ci		return err;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* The datasheet indicates the PHY needs up to 1us to complete a reset,
68862306a36Sopenharmony_ci	 * build some slack here.
68962306a36Sopenharmony_ci	 */
69062306a36Sopenharmony_ci	usleep_range(1000, 2000);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* The PHY requires 65 MDC clock cycles to complete a write operation
69362306a36Sopenharmony_ci	 * and turnaround the line properly.
69462306a36Sopenharmony_ci	 *
69562306a36Sopenharmony_ci	 * We ignore -EIO here as the MDIO controller (e.g.: mdio-bcm-unimac)
69662306a36Sopenharmony_ci	 * may flag the lack of turn-around as a read failure. This is
69762306a36Sopenharmony_ci	 * particularly true with this combination since the MDIO controller
69862306a36Sopenharmony_ci	 * only used 64 MDC cycles. This is not a critical failure in this
69962306a36Sopenharmony_ci	 * specific case and it has no functional impact otherwise, so we let
70062306a36Sopenharmony_ci	 * that one go through. If there is a genuine bus error, the next read
70162306a36Sopenharmony_ci	 * of MII_BRCM_FET_INTREG will error out.
70262306a36Sopenharmony_ci	 */
70362306a36Sopenharmony_ci	err = phy_read(phydev, MII_BMCR);
70462306a36Sopenharmony_ci	if (err < 0 && err != -EIO)
70562306a36Sopenharmony_ci		return err;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
70862306a36Sopenharmony_ci	if (reg < 0)
70962306a36Sopenharmony_ci		return reg;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* Unmask events we are interested in and mask interrupts globally. */
71262306a36Sopenharmony_ci	reg = MII_BRCM_FET_IR_DUPLEX_EN |
71362306a36Sopenharmony_ci	      MII_BRCM_FET_IR_SPEED_EN |
71462306a36Sopenharmony_ci	      MII_BRCM_FET_IR_LINK_EN |
71562306a36Sopenharmony_ci	      MII_BRCM_FET_IR_ENABLE |
71662306a36Sopenharmony_ci	      MII_BRCM_FET_IR_MASK;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
71962306a36Sopenharmony_ci	if (err < 0)
72062306a36Sopenharmony_ci		return err;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Enable shadow register access */
72362306a36Sopenharmony_ci	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
72462306a36Sopenharmony_ci	if (brcmtest < 0)
72562306a36Sopenharmony_ci		return brcmtest;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	reg = brcmtest | MII_BRCM_FET_BT_SRE;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
73062306a36Sopenharmony_ci	if (err < 0)
73162306a36Sopenharmony_ci		return err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* Set the LED mode */
73462306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
73562306a36Sopenharmony_ci	if (reg < 0) {
73662306a36Sopenharmony_ci		err = reg;
73762306a36Sopenharmony_ci		goto done;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
74162306a36Sopenharmony_ci	reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
74462306a36Sopenharmony_ci	if (err < 0)
74562306a36Sopenharmony_ci		goto done;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Enable auto MDIX */
74862306a36Sopenharmony_ci	err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
74962306a36Sopenharmony_ci			   MII_BRCM_FET_SHDW_MC_FAME);
75062306a36Sopenharmony_ci	if (err < 0)
75162306a36Sopenharmony_ci		goto done;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
75462306a36Sopenharmony_ci		/* Enable auto power down */
75562306a36Sopenharmony_ci		err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
75662306a36Sopenharmony_ci				   MII_BRCM_FET_SHDW_AS2_APDE);
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cidone:
76062306a36Sopenharmony_ci	/* Disable shadow register access */
76162306a36Sopenharmony_ci	err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
76262306a36Sopenharmony_ci	if (!err)
76362306a36Sopenharmony_ci		err = err2;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return err;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic int brcm_fet_ack_interrupt(struct phy_device *phydev)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	int reg;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* Clear pending interrupts.  */
77362306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
77462306a36Sopenharmony_ci	if (reg < 0)
77562306a36Sopenharmony_ci		return reg;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	return 0;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int brcm_fet_config_intr(struct phy_device *phydev)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	int reg, err;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BRCM_FET_INTREG);
78562306a36Sopenharmony_ci	if (reg < 0)
78662306a36Sopenharmony_ci		return reg;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
78962306a36Sopenharmony_ci		err = brcm_fet_ack_interrupt(phydev);
79062306a36Sopenharmony_ci		if (err)
79162306a36Sopenharmony_ci			return err;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		reg &= ~MII_BRCM_FET_IR_MASK;
79462306a36Sopenharmony_ci		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
79562306a36Sopenharmony_ci	} else {
79662306a36Sopenharmony_ci		reg |= MII_BRCM_FET_IR_MASK;
79762306a36Sopenharmony_ci		err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
79862306a36Sopenharmony_ci		if (err)
79962306a36Sopenharmony_ci			return err;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		err = brcm_fet_ack_interrupt(phydev);
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return err;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	int irq_status;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	irq_status = phy_read(phydev, MII_BRCM_FET_INTREG);
81262306a36Sopenharmony_ci	if (irq_status < 0) {
81362306a36Sopenharmony_ci		phy_error(phydev);
81462306a36Sopenharmony_ci		return IRQ_NONE;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (irq_status == 0)
81862306a36Sopenharmony_ci		return IRQ_NONE;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	phy_trigger_machine(phydev);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return IRQ_HANDLED;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int brcm_fet_suspend(struct phy_device *phydev)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	int reg, err, err2, brcmtest;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* We cannot use a read/modify/write here otherwise the PHY continues
83062306a36Sopenharmony_ci	 * to drive LEDs which defeats the purpose of low power mode.
83162306a36Sopenharmony_ci	 */
83262306a36Sopenharmony_ci	err = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
83362306a36Sopenharmony_ci	if (err < 0)
83462306a36Sopenharmony_ci		return err;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* Enable shadow register access */
83762306a36Sopenharmony_ci	brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
83862306a36Sopenharmony_ci	if (brcmtest < 0)
83962306a36Sopenharmony_ci		return brcmtest;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	reg = brcmtest | MII_BRCM_FET_BT_SRE;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
84462306a36Sopenharmony_ci	if (err < 0)
84562306a36Sopenharmony_ci		return err;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Set standby mode */
84862306a36Sopenharmony_ci	err = phy_modify(phydev, MII_BRCM_FET_SHDW_AUXMODE4,
84962306a36Sopenharmony_ci			 MII_BRCM_FET_SHDW_AM4_STANDBY,
85062306a36Sopenharmony_ci			 MII_BRCM_FET_SHDW_AM4_STANDBY);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Disable shadow register access */
85362306a36Sopenharmony_ci	err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
85462306a36Sopenharmony_ci	if (!err)
85562306a36Sopenharmony_ci		err = err2;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	return err;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic void bcm54xx_phy_get_wol(struct phy_device *phydev,
86162306a36Sopenharmony_ci				struct ethtool_wolinfo *wol)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	/* We cannot wake-up if we do not have a dedicated PHY interrupt line
86462306a36Sopenharmony_ci	 * or an out of band GPIO descriptor for wake-up. Zeroing
86562306a36Sopenharmony_ci	 * wol->supported allows the caller (MAC driver) to play through and
86662306a36Sopenharmony_ci	 * offer its own Wake-on-LAN scheme if available.
86762306a36Sopenharmony_ci	 */
86862306a36Sopenharmony_ci	if (!bcm54xx_phy_can_wakeup(phydev)) {
86962306a36Sopenharmony_ci		wol->supported = 0;
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	bcm_phy_get_wol(phydev, wol);
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cistatic int bcm54xx_phy_set_wol(struct phy_device *phydev,
87762306a36Sopenharmony_ci			       struct ethtool_wolinfo *wol)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	int ret;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* We cannot wake-up if we do not have a dedicated PHY interrupt line
88262306a36Sopenharmony_ci	 * or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP
88362306a36Sopenharmony_ci	 * allows the caller (MAC driver) to play through and offer its own
88462306a36Sopenharmony_ci	 * Wake-on-LAN scheme if available.
88562306a36Sopenharmony_ci	 */
88662306a36Sopenharmony_ci	if (!bcm54xx_phy_can_wakeup(phydev))
88762306a36Sopenharmony_ci		return -EOPNOTSUPP;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	ret = bcm_phy_set_wol(phydev, wol);
89062306a36Sopenharmony_ci	if (ret < 0)
89162306a36Sopenharmony_ci		return ret;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	return 0;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic int bcm54xx_phy_probe(struct phy_device *phydev)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv;
89962306a36Sopenharmony_ci	struct gpio_desc *wakeup_gpio;
90062306a36Sopenharmony_ci	int ret = 0;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
90362306a36Sopenharmony_ci	if (!priv)
90462306a36Sopenharmony_ci		return -ENOMEM;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	priv->wake_irq = -ENXIO;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	phydev->priv = priv;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	priv->stats = devm_kcalloc(&phydev->mdio.dev,
91162306a36Sopenharmony_ci				   bcm_phy_get_sset_count(phydev), sizeof(u64),
91262306a36Sopenharmony_ci				   GFP_KERNEL);
91362306a36Sopenharmony_ci	if (!priv->stats)
91462306a36Sopenharmony_ci		return -ENOMEM;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	priv->ptp = bcm_ptp_probe(phydev);
91762306a36Sopenharmony_ci	if (IS_ERR(priv->ptp))
91862306a36Sopenharmony_ci		return PTR_ERR(priv->ptp);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* We cannot utilize the _optional variant here since we want to know
92162306a36Sopenharmony_ci	 * whether the GPIO descriptor exists or not to advertise Wake-on-LAN
92262306a36Sopenharmony_ci	 * support or not.
92362306a36Sopenharmony_ci	 */
92462306a36Sopenharmony_ci	wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN);
92562306a36Sopenharmony_ci	if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER)
92662306a36Sopenharmony_ci		return PTR_ERR(wakeup_gpio);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (!IS_ERR(wakeup_gpio)) {
92962306a36Sopenharmony_ci		priv->wake_irq = gpiod_to_irq(wakeup_gpio);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		/* Dummy interrupt handler which is not enabled but is provided
93262306a36Sopenharmony_ci		 * in order for the interrupt descriptor to be fully set-up.
93362306a36Sopenharmony_ci		 */
93462306a36Sopenharmony_ci		ret = devm_request_irq(&phydev->mdio.dev, priv->wake_irq,
93562306a36Sopenharmony_ci				       bcm_phy_wol_isr,
93662306a36Sopenharmony_ci				       IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN,
93762306a36Sopenharmony_ci				       dev_name(&phydev->mdio.dev), phydev);
93862306a36Sopenharmony_ci		if (ret)
93962306a36Sopenharmony_ci			return ret;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* If we do not have a main interrupt or a side-band wake-up interrupt,
94362306a36Sopenharmony_ci	 * then the device cannot be marked as wake-up capable.
94462306a36Sopenharmony_ci	 */
94562306a36Sopenharmony_ci	if (!bcm54xx_phy_can_wakeup(phydev))
94662306a36Sopenharmony_ci		return 0;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return device_init_wakeup(&phydev->mdio.dev, true);
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_cistatic void bcm54xx_get_stats(struct phy_device *phydev,
95262306a36Sopenharmony_ci			      struct ethtool_stats *stats, u64 *data)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct bcm54xx_phy_priv *priv = phydev->priv;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	bcm_phy_get_stats(phydev, priv->stats, stats, data);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic void bcm54xx_link_change_notify(struct phy_device *phydev)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	u16 mask = MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE |
96262306a36Sopenharmony_ci		   MII_BCM54XX_EXP_EXP08_FORCE_DAC_WAKE;
96362306a36Sopenharmony_ci	int ret;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (phydev->state != PHY_RUNNING)
96662306a36Sopenharmony_ci		return;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/* Don't change the DAC wake settings if auto power down
96962306a36Sopenharmony_ci	 * is not requested.
97062306a36Sopenharmony_ci	 */
97162306a36Sopenharmony_ci	if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
97262306a36Sopenharmony_ci		return;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08);
97562306a36Sopenharmony_ci	if (ret < 0)
97662306a36Sopenharmony_ci		return;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* Enable/disable 10BaseT auto and forced early DAC wake depending
97962306a36Sopenharmony_ci	 * on the negotiated speed, those settings should only be done
98062306a36Sopenharmony_ci	 * for 10Mbits/sec.
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	if (phydev->speed == SPEED_10)
98362306a36Sopenharmony_ci		ret |= mask;
98462306a36Sopenharmony_ci	else
98562306a36Sopenharmony_ci		ret &= ~mask;
98662306a36Sopenharmony_ci	bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic struct phy_driver broadcom_drivers[] = {
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5411,
99262306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
99362306a36Sopenharmony_ci	.name		= "Broadcom BCM5411",
99462306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
99562306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
99662306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
99762306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
99862306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
99962306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
100062306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
100162306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
100262306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
100362306a36Sopenharmony_ci}, {
100462306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5421,
100562306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
100662306a36Sopenharmony_ci	.name		= "Broadcom BCM5421",
100762306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
100862306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
100962306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
101062306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
101162306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
101262306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
101362306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
101462306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
101562306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
101662306a36Sopenharmony_ci}, {
101762306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM54210E,
101862306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
101962306a36Sopenharmony_ci	.name		= "Broadcom BCM54210E",
102062306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
102162306a36Sopenharmony_ci	.flags		= PHY_ALWAYS_CALL_SUSPEND,
102262306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
102362306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
102462306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
102562306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
102662306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
102762306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
102862306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
102962306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
103062306a36Sopenharmony_ci	.suspend	= bcm54xx_suspend,
103162306a36Sopenharmony_ci	.resume		= bcm54xx_resume,
103262306a36Sopenharmony_ci	.get_wol	= bcm54xx_phy_get_wol,
103362306a36Sopenharmony_ci	.set_wol	= bcm54xx_phy_set_wol,
103462306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
103562306a36Sopenharmony_ci}, {
103662306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5461,
103762306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
103862306a36Sopenharmony_ci	.name		= "Broadcom BCM5461",
103962306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
104062306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
104162306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
104262306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
104362306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
104462306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
104562306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
104662306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
104762306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
104862306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
104962306a36Sopenharmony_ci}, {
105062306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM54612E,
105162306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
105262306a36Sopenharmony_ci	.name		= "Broadcom BCM54612E",
105362306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
105462306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
105562306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
105662306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
105762306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
105862306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
105962306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
106062306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
106162306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
106262306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
106362306a36Sopenharmony_ci}, {
106462306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM54616S,
106562306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
106662306a36Sopenharmony_ci	.name		= "Broadcom BCM54616S",
106762306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
106862306a36Sopenharmony_ci	.soft_reset     = genphy_soft_reset,
106962306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
107062306a36Sopenharmony_ci	.config_aneg	= bcm54616s_config_aneg,
107162306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
107262306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
107362306a36Sopenharmony_ci	.read_status	= bcm54616s_read_status,
107462306a36Sopenharmony_ci	.probe		= bcm54616s_probe,
107562306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
107662306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
107762306a36Sopenharmony_ci}, {
107862306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5464,
107962306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
108062306a36Sopenharmony_ci	.name		= "Broadcom BCM5464",
108162306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
108262306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
108362306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
108462306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
108562306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
108662306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
108762306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
108862306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
108962306a36Sopenharmony_ci	.suspend	= genphy_suspend,
109062306a36Sopenharmony_ci	.resume		= genphy_resume,
109162306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
109262306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
109362306a36Sopenharmony_ci}, {
109462306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5481,
109562306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
109662306a36Sopenharmony_ci	.name		= "Broadcom BCM5481",
109762306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
109862306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
109962306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
110062306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
110162306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
110262306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
110362306a36Sopenharmony_ci	.config_aneg	= bcm5481_config_aneg,
110462306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
110562306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
110662306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
110762306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
110862306a36Sopenharmony_ci}, {
110962306a36Sopenharmony_ci	.phy_id         = PHY_ID_BCM54810,
111062306a36Sopenharmony_ci	.phy_id_mask    = 0xfffffff0,
111162306a36Sopenharmony_ci	.name           = "Broadcom BCM54810",
111262306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
111362306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
111462306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
111562306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
111662306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
111762306a36Sopenharmony_ci	.read_mmd	= bcm54810_read_mmd,
111862306a36Sopenharmony_ci	.write_mmd	= bcm54810_write_mmd,
111962306a36Sopenharmony_ci	.config_init    = bcm54xx_config_init,
112062306a36Sopenharmony_ci	.config_aneg    = bcm5481_config_aneg,
112162306a36Sopenharmony_ci	.config_intr    = bcm_phy_config_intr,
112262306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
112362306a36Sopenharmony_ci	.suspend	= bcm54xx_suspend,
112462306a36Sopenharmony_ci	.resume		= bcm54xx_resume,
112562306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
112662306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
112762306a36Sopenharmony_ci}, {
112862306a36Sopenharmony_ci	.phy_id         = PHY_ID_BCM54811,
112962306a36Sopenharmony_ci	.phy_id_mask    = 0xfffffff0,
113062306a36Sopenharmony_ci	.name           = "Broadcom BCM54811",
113162306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
113262306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
113362306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
113462306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
113562306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
113662306a36Sopenharmony_ci	.config_init    = bcm54811_config_init,
113762306a36Sopenharmony_ci	.config_aneg    = bcm5481_config_aneg,
113862306a36Sopenharmony_ci	.config_intr    = bcm_phy_config_intr,
113962306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
114062306a36Sopenharmony_ci	.suspend	= bcm54xx_suspend,
114162306a36Sopenharmony_ci	.resume		= bcm54xx_resume,
114262306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
114362306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
114462306a36Sopenharmony_ci}, {
114562306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5482,
114662306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
114762306a36Sopenharmony_ci	.name		= "Broadcom BCM5482",
114862306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
114962306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
115062306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
115162306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
115262306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
115362306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
115462306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
115562306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
115662306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
115762306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
115862306a36Sopenharmony_ci}, {
115962306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM50610,
116062306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
116162306a36Sopenharmony_ci	.name		= "Broadcom BCM50610",
116262306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
116362306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
116462306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
116562306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
116662306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
116762306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
116862306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
116962306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
117062306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
117162306a36Sopenharmony_ci	.suspend	= bcm54xx_suspend,
117262306a36Sopenharmony_ci	.resume		= bcm54xx_resume,
117362306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
117462306a36Sopenharmony_ci}, {
117562306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM50610M,
117662306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
117762306a36Sopenharmony_ci	.name		= "Broadcom BCM50610M",
117862306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
117962306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
118062306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
118162306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
118262306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
118362306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
118462306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
118562306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
118662306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
118762306a36Sopenharmony_ci	.suspend	= bcm54xx_suspend,
118862306a36Sopenharmony_ci	.resume		= bcm54xx_resume,
118962306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
119062306a36Sopenharmony_ci}, {
119162306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM57780,
119262306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
119362306a36Sopenharmony_ci	.name		= "Broadcom BCM57780",
119462306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
119562306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
119662306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
119762306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
119862306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
119962306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
120062306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
120162306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
120262306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
120362306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
120462306a36Sopenharmony_ci}, {
120562306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCMAC131,
120662306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
120762306a36Sopenharmony_ci	.name		= "Broadcom BCMAC131",
120862306a36Sopenharmony_ci	/* PHY_BASIC_FEATURES */
120962306a36Sopenharmony_ci	.config_init	= brcm_fet_config_init,
121062306a36Sopenharmony_ci	.config_intr	= brcm_fet_config_intr,
121162306a36Sopenharmony_ci	.handle_interrupt = brcm_fet_handle_interrupt,
121262306a36Sopenharmony_ci	.suspend	= brcm_fet_suspend,
121362306a36Sopenharmony_ci	.resume		= brcm_fet_config_init,
121462306a36Sopenharmony_ci}, {
121562306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5241,
121662306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
121762306a36Sopenharmony_ci	.name		= "Broadcom BCM5241",
121862306a36Sopenharmony_ci	/* PHY_BASIC_FEATURES */
121962306a36Sopenharmony_ci	.config_init	= brcm_fet_config_init,
122062306a36Sopenharmony_ci	.config_intr	= brcm_fet_config_intr,
122162306a36Sopenharmony_ci	.handle_interrupt = brcm_fet_handle_interrupt,
122262306a36Sopenharmony_ci	.suspend	= brcm_fet_suspend,
122362306a36Sopenharmony_ci	.resume		= brcm_fet_config_init,
122462306a36Sopenharmony_ci}, {
122562306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM5395,
122662306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
122762306a36Sopenharmony_ci	.name		= "Broadcom BCM5395",
122862306a36Sopenharmony_ci	.flags		= PHY_IS_INTERNAL,
122962306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
123062306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
123162306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
123262306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
123362306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
123462306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
123562306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
123662306a36Sopenharmony_ci}, {
123762306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM53125,
123862306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
123962306a36Sopenharmony_ci	.name		= "Broadcom BCM53125",
124062306a36Sopenharmony_ci	.flags		= PHY_IS_INTERNAL,
124162306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
124262306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
124362306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
124462306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
124562306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
124662306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
124762306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
124862306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
124962306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
125062306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
125162306a36Sopenharmony_ci}, {
125262306a36Sopenharmony_ci	.phy_id		= PHY_ID_BCM53128,
125362306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff0,
125462306a36Sopenharmony_ci	.name		= "Broadcom BCM53128",
125562306a36Sopenharmony_ci	.flags		= PHY_IS_INTERNAL,
125662306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
125762306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
125862306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
125962306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
126062306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
126162306a36Sopenharmony_ci	.config_init	= bcm54xx_config_init,
126262306a36Sopenharmony_ci	.config_intr	= bcm_phy_config_intr,
126362306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
126462306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
126562306a36Sopenharmony_ci	.led_brightness_set	= bcm_phy_led_brightness_set,
126662306a36Sopenharmony_ci}, {
126762306a36Sopenharmony_ci	.phy_id         = PHY_ID_BCM89610,
126862306a36Sopenharmony_ci	.phy_id_mask    = 0xfffffff0,
126962306a36Sopenharmony_ci	.name           = "Broadcom BCM89610",
127062306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
127162306a36Sopenharmony_ci	.get_sset_count	= bcm_phy_get_sset_count,
127262306a36Sopenharmony_ci	.get_strings	= bcm_phy_get_strings,
127362306a36Sopenharmony_ci	.get_stats	= bcm54xx_get_stats,
127462306a36Sopenharmony_ci	.probe		= bcm54xx_phy_probe,
127562306a36Sopenharmony_ci	.config_init    = bcm54xx_config_init,
127662306a36Sopenharmony_ci	.config_intr    = bcm_phy_config_intr,
127762306a36Sopenharmony_ci	.handle_interrupt = bcm_phy_handle_interrupt,
127862306a36Sopenharmony_ci	.link_change_notify	= bcm54xx_link_change_notify,
127962306a36Sopenharmony_ci} };
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cimodule_phy_driver(broadcom_drivers);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused broadcom_tbl[] = {
128462306a36Sopenharmony_ci	{ PHY_ID_BCM5411, 0xfffffff0 },
128562306a36Sopenharmony_ci	{ PHY_ID_BCM5421, 0xfffffff0 },
128662306a36Sopenharmony_ci	{ PHY_ID_BCM54210E, 0xfffffff0 },
128762306a36Sopenharmony_ci	{ PHY_ID_BCM5461, 0xfffffff0 },
128862306a36Sopenharmony_ci	{ PHY_ID_BCM54612E, 0xfffffff0 },
128962306a36Sopenharmony_ci	{ PHY_ID_BCM54616S, 0xfffffff0 },
129062306a36Sopenharmony_ci	{ PHY_ID_BCM5464, 0xfffffff0 },
129162306a36Sopenharmony_ci	{ PHY_ID_BCM5481, 0xfffffff0 },
129262306a36Sopenharmony_ci	{ PHY_ID_BCM54810, 0xfffffff0 },
129362306a36Sopenharmony_ci	{ PHY_ID_BCM54811, 0xfffffff0 },
129462306a36Sopenharmony_ci	{ PHY_ID_BCM5482, 0xfffffff0 },
129562306a36Sopenharmony_ci	{ PHY_ID_BCM50610, 0xfffffff0 },
129662306a36Sopenharmony_ci	{ PHY_ID_BCM50610M, 0xfffffff0 },
129762306a36Sopenharmony_ci	{ PHY_ID_BCM57780, 0xfffffff0 },
129862306a36Sopenharmony_ci	{ PHY_ID_BCMAC131, 0xfffffff0 },
129962306a36Sopenharmony_ci	{ PHY_ID_BCM5241, 0xfffffff0 },
130062306a36Sopenharmony_ci	{ PHY_ID_BCM5395, 0xfffffff0 },
130162306a36Sopenharmony_ci	{ PHY_ID_BCM53125, 0xfffffff0 },
130262306a36Sopenharmony_ci	{ PHY_ID_BCM53128, 0xfffffff0 },
130362306a36Sopenharmony_ci	{ PHY_ID_BCM89610, 0xfffffff0 },
130462306a36Sopenharmony_ci	{ }
130562306a36Sopenharmony_ci};
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, broadcom_tbl);
1308