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