net: phy: broadcom: Add support code for downshift/Wirespeed
authorFlorian Fainelli <f.fainelli@gmail.com>
Tue, 22 Nov 2016 19:40:55 +0000 (11:40 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Nov 2016 20:45:53 +0000 (15:45 -0500)
Broadcom's Wirespeed feature allows us to configure how auto-negotiation
should behave with fewer working pairs of wires on a cable. Add support
code for retrieving and setting such downshift counters using the
recently added ethtool downshift tunables.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/bcm-phy-lib.c
drivers/net/phy/bcm-phy-lib.h
include/linux/brcmphy.h

index 18e11b3..d742894 100644 (file)
@@ -225,6 +225,92 @@ int bcm_phy_enable_eee(struct phy_device *phydev)
 }
 EXPORT_SYMBOL_GPL(bcm_phy_enable_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_LICENSE("GPL v2");
 MODULE_AUTHOR("Broadcom Corporation");
index 31cb4fd..3f492e6 100644 (file)
@@ -37,4 +37,9 @@ 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_eee(struct phy_device *phydev);
+
+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 */
index 848dc50..f9f8aaf 100644 (file)
 #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC        0x0007
 #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_WIRESPEED_EN   (1 << 4)
 
 #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK        0x0007
 
 #define BCM_LED_SRC_INTR       0x6
 #define BCM_LED_SRC_QUALITY    0x7
 #define BCM_LED_SRC_RCVLED     0x8
+#define BCM_LED_SRC_WIRESPEED  0x9
 #define BCM_LED_SRC_MULTICOLOR1        0xa
 #define BCM_LED_SRC_OPENSHORT  0xb
 #define BCM_LED_SRC_OFF                0xe     /* Tied high */
  * Shadow values go into bits [14:10] of register 0x1c to select a shadow
  * 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 */
 #define BCM54XX_SHD_SCR3               0x05
 #define  BCM54XX_SHD_SCR3_DEF_CLK125   0x0001