Merge branch 'phy-broadcom-wirespeed-downshift-support'
Florian Fainelli says: ==================== net: phy: broadcom: Wirespeed/downshift support This patch series adds support for the Broadcom Wirespeed, aka downsfhit feature utilizing the recently added ethtool PHY tunables. Tested with two Gigabit link partners with a 4-wire cable having only 2 pairs connected. Last patch in the series is a fix that was required for testing, which should make it to -stable, which I can submit separate against net if you prefer David. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
ac32378f3e
|
@ -588,6 +588,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
|
||||||
struct phy_device *phydev)
|
struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
|
||||||
|
struct ethtool_eee *p = &priv->port_sts[port].eee;
|
||||||
u32 id_mode_dis = 0, port_mode;
|
u32 id_mode_dis = 0, port_mode;
|
||||||
const char *str = NULL;
|
const char *str = NULL;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
@ -662,6 +663,9 @@ force_link:
|
||||||
reg |= DUPLX_MODE;
|
reg |= DUPLX_MODE;
|
||||||
|
|
||||||
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
|
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
|
||||||
|
|
||||||
|
if (!phydev->is_pseudo_fixed_link)
|
||||||
|
p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
|
||||||
|
|
|
@ -104,7 +104,7 @@ static int bcm_cygnus_config_init(struct phy_device *phydev)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
/* Advertise EEE */
|
/* Advertise EEE */
|
||||||
rc = bcm_phy_enable_eee(phydev);
|
rc = bcm_phy_set_eee(phydev, true);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,23 @@ int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
|
EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
|
||||||
|
|
||||||
|
int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
|
||||||
|
{
|
||||||
|
/* The register must be written to both the Shadow Register Select and
|
||||||
|
* the Shadow Read Register Selector
|
||||||
|
*/
|
||||||
|
phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum |
|
||||||
|
regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
|
||||||
|
return phy_read(phydev, MII_BCM54XX_AUX_CTL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
|
||||||
|
|
||||||
|
int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
|
||||||
|
{
|
||||||
|
return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(bcm54xx_auxctl_write);
|
||||||
|
|
||||||
int bcm_phy_write_misc(struct phy_device *phydev,
|
int bcm_phy_write_misc(struct phy_device *phydev,
|
||||||
u16 reg, u16 chl, u16 val)
|
u16 reg, u16 chl, u16 val)
|
||||||
{
|
{
|
||||||
|
@ -178,7 +195,7 @@ int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
|
EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
|
||||||
|
|
||||||
int bcm_phy_enable_eee(struct phy_device *phydev)
|
int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
|
||||||
{
|
{
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
|
@ -188,7 +205,10 @@ int bcm_phy_enable_eee(struct phy_device *phydev)
|
||||||
if (val < 0)
|
if (val < 0)
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
|
val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
|
||||||
|
else
|
||||||
|
val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
|
||||||
|
|
||||||
phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
|
phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
|
||||||
MDIO_MMD_AN, (u32)val);
|
MDIO_MMD_AN, (u32)val);
|
||||||
|
@ -199,14 +219,103 @@ int bcm_phy_enable_eee(struct phy_device *phydev)
|
||||||
if (val < 0)
|
if (val < 0)
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
|
val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
|
||||||
|
else
|
||||||
|
val &= ~(MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
|
||||||
|
|
||||||
phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
|
phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
|
||||||
MDIO_MMD_AN, (u32)val);
|
MDIO_MMD_AN, (u32)val);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);
|
EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
|
||||||
|
|
||||||
|
int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
|
||||||
|
val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
|
||||||
|
if (val < 0)
|
||||||
|
return val;
|
||||||
|
|
||||||
|
/* Check if wirespeed is enabled or not */
|
||||||
|
if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
|
||||||
|
*count = DOWNSHIFT_DEV_DISABLE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
|
||||||
|
if (val < 0)
|
||||||
|
return val;
|
||||||
|
|
||||||
|
/* Downgrade after one link attempt */
|
||||||
|
if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
|
||||||
|
*count = 1;
|
||||||
|
} else {
|
||||||
|
/* Downgrade after configured retry count */
|
||||||
|
val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
|
||||||
|
val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
|
||||||
|
*count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
|
||||||
|
|
||||||
|
int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
|
||||||
|
{
|
||||||
|
int val = 0, ret = 0;
|
||||||
|
|
||||||
|
/* Range check the number given */
|
||||||
|
if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
|
||||||
|
BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
|
||||||
|
count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
|
||||||
|
if (val < 0)
|
||||||
|
return val;
|
||||||
|
|
||||||
|
/* Se the write enable bit */
|
||||||
|
val |= MII_BCM54XX_AUXCTL_MISC_WREN;
|
||||||
|
|
||||||
|
if (count == DOWNSHIFT_DEV_DISABLE) {
|
||||||
|
val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
|
||||||
|
return bcm54xx_auxctl_write(phydev,
|
||||||
|
MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
|
||||||
|
val);
|
||||||
|
} else {
|
||||||
|
val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
|
||||||
|
ret = bcm54xx_auxctl_write(phydev,
|
||||||
|
MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
|
||||||
|
val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
|
||||||
|
val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
|
||||||
|
BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
|
||||||
|
BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
|
||||||
|
|
||||||
|
switch (count) {
|
||||||
|
case 1:
|
||||||
|
val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
|
||||||
|
break;
|
||||||
|
case DOWNSHIFT_DEV_DEFAULT_COUNT:
|
||||||
|
val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
|
||||||
|
BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Broadcom PHY Library");
|
MODULE_DESCRIPTION("Broadcom PHY Library");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val);
|
int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val);
|
||||||
int bcm_phy_read_exp(struct phy_device *phydev, u16 reg);
|
int bcm_phy_read_exp(struct phy_device *phydev, u16 reg);
|
||||||
|
|
||||||
|
int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val);
|
||||||
|
int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum);
|
||||||
|
|
||||||
int bcm_phy_write_misc(struct phy_device *phydev,
|
int bcm_phy_write_misc(struct phy_device *phydev,
|
||||||
u16 reg, u16 chl, u16 value);
|
u16 reg, u16 chl, u16 value);
|
||||||
int bcm_phy_read_misc(struct phy_device *phydev,
|
int bcm_phy_read_misc(struct phy_device *phydev,
|
||||||
|
@ -33,5 +36,10 @@ int bcm_phy_config_intr(struct phy_device *phydev);
|
||||||
|
|
||||||
int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down);
|
int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down);
|
||||||
|
|
||||||
int bcm_phy_enable_eee(struct phy_device *phydev);
|
int bcm_phy_set_eee(struct phy_device *phydev, bool enable);
|
||||||
|
|
||||||
|
int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count);
|
||||||
|
|
||||||
|
int bcm_phy_downshift_set(struct phy_device *phydev, u8 count);
|
||||||
|
|
||||||
#endif /* _LINUX_BCM_PHY_LIB_H */
|
#endif /* _LINUX_BCM_PHY_LIB_H */
|
||||||
|
|
|
@ -167,6 +167,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
|
u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
|
||||||
u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
|
u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
|
||||||
|
u8 count;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
|
pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
|
||||||
|
@ -199,7 +200,12 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = bcm_phy_enable_eee(phydev);
|
ret = bcm_phy_downshift_get(phydev, &count);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Only enable EEE if Wirespeed/downshift is disabled */
|
||||||
|
ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -303,6 +309,47 @@ static int bcm7xxx_suspend(struct phy_device *phydev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
|
||||||
|
struct ethtool_tunable *tuna,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
switch (tuna->id) {
|
||||||
|
case ETHTOOL_PHY_DOWNSHIFT:
|
||||||
|
return bcm_phy_downshift_get(phydev, (u8 *)data);
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
|
||||||
|
struct ethtool_tunable *tuna,
|
||||||
|
const void *data)
|
||||||
|
{
|
||||||
|
u8 count = *(u8 *)data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (tuna->id) {
|
||||||
|
case ETHTOOL_PHY_DOWNSHIFT:
|
||||||
|
ret = bcm_phy_downshift_set(phydev, count);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Disable EEE advertisment since this prevents the PHY
|
||||||
|
* from successfully linking up, trigger auto-negotiation restart
|
||||||
|
* to let the MAC decide what to do.
|
||||||
|
*/
|
||||||
|
ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return genphy_restart_aneg(phydev);
|
||||||
|
}
|
||||||
|
|
||||||
#define BCM7XXX_28NM_GPHY(_oui, _name) \
|
#define BCM7XXX_28NM_GPHY(_oui, _name) \
|
||||||
{ \
|
{ \
|
||||||
.phy_id = (_oui), \
|
.phy_id = (_oui), \
|
||||||
|
@ -315,6 +362,8 @@ static int bcm7xxx_suspend(struct phy_device *phydev)
|
||||||
.config_aneg = genphy_config_aneg, \
|
.config_aneg = genphy_config_aneg, \
|
||||||
.read_status = genphy_read_status, \
|
.read_status = genphy_read_status, \
|
||||||
.resume = bcm7xxx_28nm_resume, \
|
.resume = bcm7xxx_28nm_resume, \
|
||||||
|
.get_tunable = bcm7xxx_28nm_get_tunable, \
|
||||||
|
.set_tunable = bcm7xxx_28nm_set_tunable, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BCM7XXX_40NM_EPHY(_oui, _name) \
|
#define BCM7XXX_40NM_EPHY(_oui, _name) \
|
||||||
|
|
|
@ -30,21 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver");
|
||||||
MODULE_AUTHOR("Maciej W. Rozycki");
|
MODULE_AUTHOR("Maciej W. Rozycki");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
|
|
||||||
{
|
|
||||||
/* The register must be written to both the Shadow Register Select and
|
|
||||||
* the Shadow Read Register Selector
|
|
||||||
*/
|
|
||||||
phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum |
|
|
||||||
regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
|
|
||||||
return phy_read(phydev, MII_BCM54XX_AUX_CTL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
|
|
||||||
{
|
|
||||||
return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bcm54810_config(struct phy_device *phydev)
|
static int bcm54810_config(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
int rc, val;
|
int rc, val;
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
|
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
|
||||||
#define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12
|
#define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12
|
||||||
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8)
|
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8)
|
||||||
|
#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN (1 << 4)
|
||||||
|
|
||||||
#define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007
|
#define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007
|
||||||
|
|
||||||
|
@ -130,6 +131,7 @@
|
||||||
#define BCM_LED_SRC_INTR 0x6
|
#define BCM_LED_SRC_INTR 0x6
|
||||||
#define BCM_LED_SRC_QUALITY 0x7
|
#define BCM_LED_SRC_QUALITY 0x7
|
||||||
#define BCM_LED_SRC_RCVLED 0x8
|
#define BCM_LED_SRC_RCVLED 0x8
|
||||||
|
#define BCM_LED_SRC_WIRESPEED 0x9
|
||||||
#define BCM_LED_SRC_MULTICOLOR1 0xa
|
#define BCM_LED_SRC_MULTICOLOR1 0xa
|
||||||
#define BCM_LED_SRC_OPENSHORT 0xb
|
#define BCM_LED_SRC_OPENSHORT 0xb
|
||||||
#define BCM_LED_SRC_OFF 0xe /* Tied high */
|
#define BCM_LED_SRC_OFF 0xe /* Tied high */
|
||||||
|
@ -141,6 +143,14 @@
|
||||||
* Shadow values go into bits [14:10] of register 0x1c to select a shadow
|
* Shadow values go into bits [14:10] of register 0x1c to select a shadow
|
||||||
* register to access.
|
* register to access.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 00100: Reserved control register 2 */
|
||||||
|
#define BCM54XX_SHD_SCR2 0x04
|
||||||
|
#define BCM54XX_SHD_SCR2_WSPD_RTRY_DIS 0x100
|
||||||
|
#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT 2
|
||||||
|
#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET 2
|
||||||
|
#define BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK 0x7
|
||||||
|
|
||||||
/* 00101: Spare Control Register 3 */
|
/* 00101: Spare Control Register 3 */
|
||||||
#define BCM54XX_SHD_SCR3 0x05
|
#define BCM54XX_SHD_SCR3 0x05
|
||||||
#define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001
|
#define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001
|
||||||
|
|
Loading…
Reference in New Issue