// SPDX-License-Identifier: GPL-2.0+/* * drivers/net/phy/broadcom.c * * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet * transceivers. * * Copyright (c) 2006 Maciej W. Rozycki * * Inspired by code written by Amy Fong. */ #include "bcm-phy-lib.h"#include <linux/module.h>#include <linux/phy.h>#include <linux/brcmphy.h>#include <linux/of.h> #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) #define BRCM_PHY_REV(phydev) \ ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask)) MODULE_DESCRIPTION("Broadcom PHY driver");MODULE_AUTHOR("Maciej W. Rozycki");MODULE_LICENSE("GPL"); static int bcm54xx_config_clock_delay(struct phy_device *phydev){ int rc, val; /* handling PHY's internal RX clock delay */ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); val |= MII_BCM54XX_AUXCTL_MISC_WREN; if (phydev->interface == PHY_INTERFACE_MODE_RGMII || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { /* Disable RGMII RXC-RXD skew */ val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; } if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { /* Enable RGMII RXC-RXD skew */ val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; } rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val); if (rc < 0) return rc; /* handling PHY's internal TX clock delay */ val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { /* Disable internal TX clock delay */ val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; } if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { /* Enable internal TX clock delay */ val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN; } rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); if (rc < 0) return rc; return 0;} static int bcm54210e_config_init(struct phy_device *phydev){ int val; bcm54xx_config_clock_delay(phydev); if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { val = phy_read(phydev, MII_CTRL1000); val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; phy_write(phydev, MII_CTRL1000, val); } return 0;} static int bcm54612e_config_init(struct phy_device *phydev){ int reg; bcm54xx_config_clock_delay(phydev); /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { int err; reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, BCM54612E_LED4_CLK125OUT_EN | reg); if (err < 0) return err; } return 0;} static int bcm54616s_config_init(struct phy_device *phydev){ int rc, val; if (phydev->interface != PHY_INTERFACE_MODE_SGMII && phydev->interface != PHY_INTERFACE_MODE_1000BASEX) return 0; /* Ensure proper interface mode is selected. */ /* Disable RGMII mode */ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); if (val < 0) return val; val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN; val |= MII_BCM54XX_AUXCTL_MISC_WREN; rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val); if (rc < 0) return rc; /* Select 1000BASE-X register set (primary SerDes) */ val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); if (val < 0) return val; val |= BCM54XX_SHD_MODE_1000BX; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power down SerDes interface */ rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN); if (rc < 0) return rc; /* Select proper interface mode */ val &= ~BCM54XX_SHD_INTF_SEL_MASK; val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ? BCM54XX_SHD_INTF_SEL_SGMII : BCM54XX_SHD_INTF_SEL_GBIC; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power up SerDes interface */ rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN); if (rc < 0) return rc; /* Select copper register set */ val &= ~BCM54XX_SHD_MODE_1000BX; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power up copper interface */ return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);} /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */static int bcm50610_a0_workaround(struct phy_device *phydev){ int err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0, MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, MII_BCM54XX_EXP_EXP75_VDACCTRL); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96, MII_BCM54XX_EXP_EXP96_MYST); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97, MII_BCM54XX_EXP_EXP97_MYST); return err;} static int bcm54xx_phydsp_config(struct phy_device *phydev){ int err, err2; /* Enable the SMDSP clock */ err = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | MII_BCM54XX_AUXCTL_ACTL_TX_6DB); if (err < 0) return err; if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { /* Clear bit 9 to fix a phy interop issue. */ err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); if (err < 0) goto error; if (phydev->drv->phy_id == PHY_ID_BCM50610) { err = bcm50610_a0_workaround(phydev); if (err < 0) goto error; } } if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { int val; val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75); if (val < 0) goto error; val |= MII_BCM54XX_EXP_EXP75_CM_OSC; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val); } error: /* Disable the SMDSP clock */ err2 = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, MII_BCM54XX_AUXCTL_ACTL_TX_6DB); /* Return the first error reported. */ return err ? err : err2;} static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev){ u32 orig; int val; bool clk125en = true; /* Abort if we are using an untested phy. */ if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) return; val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); if (val < 0) return; orig = val; if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && BRCM_PHY_REV(phydev) >= 0x3) { /* * Here, bit 0 _disables_ CLK125 when set. * This bit is set by default. */ clk125en = false; } else { if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) { if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) { /* Here, bit 0 _enables_ CLK125 when set */ val &= ~BCM54XX_SHD_SCR3_DEF_CLK125; } clk125en = false; } } if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS; else val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) { if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) val |= BCM54XX_SHD_SCR3_RXCTXC_DIS; else val |= BCM54XX_SHD_SCR3_TRDDAPD; } if (orig != val) bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); if (val < 0) return; orig = val; if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) val |= BCM54XX_SHD_APD_EN; else val &= ~BCM54XX_SHD_APD_EN; if (orig != val) bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);} static int bcm54xx_config_init(struct phy_device *phydev){ int reg, err, val; reg = phy_read(phydev, MII_BCM54XX_ECR); if (reg < 0) return reg; /* Mask interrupts globally. */ reg |= MII_BCM54XX_ECR_IM; err = phy_write(phydev, MII_BCM54XX_ECR, reg); if (err < 0) return err; /* Unmask events we are interested in. */ reg = ~(MII_BCM54XX_INT_DUPLEX | MII_BCM54XX_INT_SPEED | MII_BCM54XX_INT_LINK); err = phy_write(phydev, MII_BCM54XX_IMR, reg); if (err < 0) return err; if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0); bcm54xx_adjust_rxrefclk(phydev); switch (BRCM_PHY_MODEL(phydev)) { case PHY_ID_BCM50610: case PHY_ID_BCM50610M: err = bcm54xx_config_clock_delay(phydev); break; case PHY_ID_BCM54210E: err = bcm54210e_config_init(phydev); break; case PHY_ID_BCM54612E: err = bcm54612e_config_init(phydev); break; case PHY_ID_BCM54616S: err = bcm54616s_config_init(phydev); break; case PHY_ID_BCM54810: /* For BCM54810, we need to disable BroadR-Reach function */ val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, val); break; } if (err) return err; bcm54xx_phydsp_config(phydev); /* For non-SFP setups, encode link speed into LED1 and LED3 pair * (green/amber). * Also flash these two LEDs on activity. This means configuring * them for MULTICOLOR and encoding link/activity into them. * Don't do this for devices on an SFP module, since some of these * use the LED outputs to control the SFP LOS signal, and changing * these settings will cause LOS to malfunction. */ if (!phy_on_sfp(phydev)) { val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) | BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1); bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val); val = BCM_LED_MULTICOLOR_IN_PHASE | BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) | BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT); bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val); } return 0;} static int bcm54xx_resume(struct phy_device *phydev){ int ret; /* Writes to register other than BMCR would be ignored * unless we clear the PDOWN bit first */ ret = genphy_resume(phydev); if (ret < 0) return ret; /* Upon exiting power down, the PHY remains in an internal reset state * for 40us */ fsleep(40); return bcm54xx_config_init(phydev);} static int bcm54811_config_init(struct phy_device *phydev){ int err, reg; /* Disable BroadR-Reach function. */ reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg); if (err < 0) return err; err = bcm54xx_config_init(phydev); /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, BCM54612E_LED4_CLK125OUT_EN | reg); if (err < 0) return err; } return err;} static int bcm5481_config_aneg(struct phy_device *phydev){ struct device_node *np = phydev->mdio.dev.of_node; int ret; /* Aneg firstly. */ ret = genphy_config_aneg(phydev); /* Then we can set up the delay. */ bcm54xx_config_clock_delay(phydev); if (of_property_read_bool(np, "enet-phy-lane-swap")) { /* Lane Swap - Undocumented register...magic! */ ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9, 0x11B); if (ret < 0) return ret; } return ret;} struct bcm54616s_phy_priv { bool mode_1000bx_en;}; static int bcm54616s_probe(struct phy_device *phydev){ struct bcm54616s_phy_priv *priv; int val; priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; phydev->priv = priv; val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); if (val < 0) return val; /* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0] * is 01b, and the link between PHY and its link partner can be * either 1000Base-X or 100Base-FX. * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX * support is still missing as of now. */ if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) { val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL); if (val < 0) return val; /* Bit 0 of the SerDes 100-FX Control register, when set * to 1, sets the MII/RGMII -> 100BASE-FX configuration. * When this bit is set to 0, it sets the GMII/RGMII -> * 1000BASE-X configuration. */ if (!(val & BCM54616S_100FX_MODE)) priv->mode_1000bx_en = true; phydev->port = PORT_FIBRE;Lines 1–500 of 960. Load more
// SPDX-License-Identifier: GPL-2.0+ /* * drivers/net/phy/broadcom.c * * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet * transceivers. * * Copyright (c) 2006 Maciej W. Rozycki * * Inspired by code written by Amy Fong. */ #include "bcm-phy-lib.h" #include <linux/module.h> #include <linux/phy.h> #include <linux/brcmphy.h> #include <linux/of.h> #define BRCM_PHY_MODEL(phydev) \ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) #define BRCM_PHY_REV(phydev) \ ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask)) MODULE_DESCRIPTION("Broadcom PHY driver"); MODULE_AUTHOR("Maciej W. Rozycki"); MODULE_LICENSE("GPL"); static int bcm54xx_config_clock_delay(struct phy_device *phydev) { int rc, val; /* handling PHY's internal RX clock delay */ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); val |= MII_BCM54XX_AUXCTL_MISC_WREN; if (phydev->interface == PHY_INTERFACE_MODE_RGMII || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { /* Disable RGMII RXC-RXD skew */ val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; } if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { /* Enable RGMII RXC-RXD skew */ val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; } rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val); if (rc < 0) return rc; /* handling PHY's internal TX clock delay */ val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII || phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { /* Disable internal TX clock delay */ val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; } if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { /* Enable internal TX clock delay */ val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN; } rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); if (rc < 0) return rc; return 0; } static int bcm54210e_config_init(struct phy_device *phydev) { int val; bcm54xx_config_clock_delay(phydev); if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { val = phy_read(phydev, MII_CTRL1000); val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; phy_write(phydev, MII_CTRL1000, val); } return 0; } static int bcm54612e_config_init(struct phy_device *phydev) { int reg; bcm54xx_config_clock_delay(phydev); /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { int err; reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, BCM54612E_LED4_CLK125OUT_EN | reg); if (err < 0) return err; } return 0; } static int bcm54616s_config_init(struct phy_device *phydev) { int rc, val; if (phydev->interface != PHY_INTERFACE_MODE_SGMII && phydev->interface != PHY_INTERFACE_MODE_1000BASEX) return 0; /* Ensure proper interface mode is selected. */ /* Disable RGMII mode */ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); if (val < 0) return val; val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN; val |= MII_BCM54XX_AUXCTL_MISC_WREN; rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val); if (rc < 0) return rc; /* Select 1000BASE-X register set (primary SerDes) */ val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); if (val < 0) return val; val |= BCM54XX_SHD_MODE_1000BX; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power down SerDes interface */ rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN); if (rc < 0) return rc; /* Select proper interface mode */ val &= ~BCM54XX_SHD_INTF_SEL_MASK; val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ? BCM54XX_SHD_INTF_SEL_SGMII : BCM54XX_SHD_INTF_SEL_GBIC; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power up SerDes interface */ rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN); if (rc < 0) return rc; /* Select copper register set */ val &= ~BCM54XX_SHD_MODE_1000BX; rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val); if (rc < 0) return rc; /* Power up copper interface */ return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN); } /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */ static int bcm50610_a0_workaround(struct phy_device *phydev) { int err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0, MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN | MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3, MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, MII_BCM54XX_EXP_EXP75_VDACCTRL); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96, MII_BCM54XX_EXP_EXP96_MYST); if (err < 0) return err; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97, MII_BCM54XX_EXP_EXP97_MYST); return err; } static int bcm54xx_phydsp_config(struct phy_device *phydev) { int err, err2; /* Enable the SMDSP clock */ err = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA | MII_BCM54XX_AUXCTL_ACTL_TX_6DB); if (err < 0) return err; if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) { /* Clear bit 9 to fix a phy interop issue. */ err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, MII_BCM54XX_EXP_EXP08_RJCT_2MHZ); if (err < 0) goto error; if (phydev->drv->phy_id == PHY_ID_BCM50610) { err = bcm50610_a0_workaround(phydev); if (err < 0) goto error; } } if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) { int val; val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75); if (val < 0) goto error; val |= MII_BCM54XX_EXP_EXP75_CM_OSC; err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val); } error: /* Disable the SMDSP clock */ err2 = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, MII_BCM54XX_AUXCTL_ACTL_TX_6DB); /* Return the first error reported. */ return err ? err : err2; } static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) { u32 orig; int val; bool clk125en = true; /* Abort if we are using an untested phy. */ if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 && BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) return; val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); if (val < 0) return; orig = val; if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && BRCM_PHY_REV(phydev) >= 0x3) { /* * Here, bit 0 _disables_ CLK125 when set. * This bit is set by default. */ clk125en = false; } else { if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) { if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) { /* Here, bit 0 _enables_ CLK125 when set */ val &= ~BCM54XX_SHD_SCR3_DEF_CLK125; } clk125en = false; } } if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS; else val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) { if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) val |= BCM54XX_SHD_SCR3_RXCTXC_DIS; else val |= BCM54XX_SHD_SCR3_TRDDAPD; } if (orig != val) bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); if (val < 0) return; orig = val; if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE)) val |= BCM54XX_SHD_APD_EN; else val &= ~BCM54XX_SHD_APD_EN; if (orig != val) bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); } static int bcm54xx_config_init(struct phy_device *phydev) { int reg, err, val; reg = phy_read(phydev, MII_BCM54XX_ECR); if (reg < 0) return reg; /* Mask interrupts globally. */ reg |= MII_BCM54XX_ECR_IM; err = phy_write(phydev, MII_BCM54XX_ECR, reg); if (err < 0) return err; /* Unmask events we are interested in. */ reg = ~(MII_BCM54XX_INT_DUPLEX | MII_BCM54XX_INT_SPEED | MII_BCM54XX_INT_LINK); err = phy_write(phydev, MII_BCM54XX_IMR, reg); if (err < 0) return err; if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 || BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) && (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE)) bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0); bcm54xx_adjust_rxrefclk(phydev); switch (BRCM_PHY_MODEL(phydev)) { case PHY_ID_BCM50610: case PHY_ID_BCM50610M: err = bcm54xx_config_clock_delay(phydev); break; case PHY_ID_BCM54210E: err = bcm54210e_config_init(phydev); break; case PHY_ID_BCM54612E: err = bcm54612e_config_init(phydev); break; case PHY_ID_BCM54616S: err = bcm54616s_config_init(phydev); break; case PHY_ID_BCM54810: /* For BCM54810, we need to disable BroadR-Reach function */ val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, val); break; } if (err) return err; bcm54xx_phydsp_config(phydev); /* For non-SFP setups, encode link speed into LED1 and LED3 pair * (green/amber). * Also flash these two LEDs on activity. This means configuring * them for MULTICOLOR and encoding link/activity into them. * Don't do this for devices on an SFP module, since some of these * use the LED outputs to control the SFP LOS signal, and changing * these settings will cause LOS to malfunction. */ if (!phy_on_sfp(phydev)) { val = BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) | BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1); bcm_phy_write_shadow(phydev, BCM5482_SHD_LEDS1, val); val = BCM_LED_MULTICOLOR_IN_PHASE | BCM5482_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) | BCM5482_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT); bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val); } return 0; } static int bcm54xx_resume(struct phy_device *phydev) { int ret; /* Writes to register other than BMCR would be ignored * unless we clear the PDOWN bit first */ ret = genphy_resume(phydev); if (ret < 0) return ret; /* Upon exiting power down, the PHY remains in an internal reset state * for 40us */ fsleep(40); return bcm54xx_config_init(phydev); } static int bcm54811_config_init(struct phy_device *phydev) { int err, reg; /* Disable BroadR-Reach function. */ reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg); if (err < 0) return err; err = bcm54xx_config_init(phydev); /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, BCM54612E_LED4_CLK125OUT_EN | reg); if (err < 0) return err; } return err; } static int bcm5481_config_aneg(struct phy_device *phydev) { struct device_node *np = phydev->mdio.dev.of_node; int ret; /* Aneg firstly. */ ret = genphy_config_aneg(phydev); /* Then we can set up the delay. */ bcm54xx_config_clock_delay(phydev); if (of_property_read_bool(np, "enet-phy-lane-swap")) { /* Lane Swap - Undocumented register...magic! */ ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9, 0x11B); if (ret < 0) return ret; } return ret; } struct bcm54616s_phy_priv { bool mode_1000bx_en; }; static int bcm54616s_probe(struct phy_device *phydev) { struct bcm54616s_phy_priv *priv; int val; priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; phydev->priv = priv; val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE); if (val < 0) return val; /* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0] * is 01b, and the link between PHY and its link partner can be * either 1000Base-X or 100Base-FX. * RGMII-1000Base-X is properly supported, but RGMII-100Base-FX * support is still missing as of now. */ if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) { val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL); if (val < 0) return val; /* Bit 0 of the SerDes 100-FX Control register, when set * to 1, sets the MII/RGMII -> 100BASE-FX configuration. * When this bit is set to 0, it sets the GMII/RGMII -> * 1000BASE-X configuration. */ if (!(val & BCM54616S_100FX_MODE)) priv->mode_1000bx_en = true; phydev->port = PORT_FIBRE;