Skip to content

Commit

Permalink
net: phy: broadcom: Set proper 1000BaseX/SGMII interface mode for BCM…
Browse files Browse the repository at this point in the history
…54616S

The default configuration for the BCM54616S PHY may not match the desired
mode when using 1000BaseX or SGMII interface modes, such as when it is on
an SFP module. Add code to explicitly set the correct mode using
programming sequences provided by Bel-Fuse:

https://www.belfuse.com/resources/datasheets/powersolutions/ds-bps-sfp-1gbt-05-series.pdf
https://www.belfuse.com/resources/datasheets/powersolutions/ds-bps-sfp-1gbt-06-series.pdf

Signed-off-by: Robert Hancock <robert.hancock@calian.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Robert Hancock authored and David S. Miller committed Feb 16, 2021
1 parent 966df6d commit 3afd021
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 12 deletions.
84 changes: 72 additions & 12 deletions drivers/net/phy/broadcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,64 @@ static int bcm54612e_config_init(struct phy_device *phydev)
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)
{
Expand Down Expand Up @@ -283,25 +341,28 @@ static int bcm54xx_config_init(struct phy_device *phydev)

bcm54xx_adjust_rxrefclk(phydev);

if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) {
switch (BRCM_PHY_MODEL(phydev)) {
case PHY_ID_BCM54210E:
err = bcm54210e_config_init(phydev);
if (err)
return err;
} else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54612E) {
break;
case PHY_ID_BCM54612E:
err = bcm54612e_config_init(phydev);
if (err)
return err;
} else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
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);
if (err < 0)
return err;
break;
}
if (err)
return err;

bcm54xx_phydsp_config(phydev);

Expand Down Expand Up @@ -390,7 +451,7 @@ struct bcm54616s_phy_priv {
static int bcm54616s_probe(struct phy_device *phydev)
{
struct bcm54616s_phy_priv *priv;
int val, intf_sel;
int val;

priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
Expand All @@ -408,8 +469,7 @@ static int bcm54616s_probe(struct phy_device *phydev)
* RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
* support is still missing as of now.
*/
intf_sel = (val & BCM54XX_SHD_INTF_SEL_MASK) >> 1;
if (intf_sel == 1) {
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;
Expand Down
4 changes: 4 additions & 0 deletions include/linux/brcmphy.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@

#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x07
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN 0x0010
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN 0x0080
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN 0x0100
#define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
#define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
Expand Down Expand Up @@ -216,6 +217,9 @@
/* 11111: Mode Control Register */
#define BCM54XX_SHD_MODE 0x1f
#define BCM54XX_SHD_INTF_SEL_MASK GENMASK(2, 1) /* INTERF_SEL[1:0] */
#define BCM54XX_SHD_INTF_SEL_RGMII 0x02
#define BCM54XX_SHD_INTF_SEL_SGMII 0x04
#define BCM54XX_SHD_INTF_SEL_GBIC 0x06
#define BCM54XX_SHD_MODE_1000BX BIT(0) /* Enable 1000-X registers */

/*
Expand Down

0 comments on commit 3afd021

Please sign in to comment.