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