1a1cba561SArun Parameswaran /* 2cda792c3SDoug Berger * Copyright (C) 2015-2017 Broadcom 3a1cba561SArun Parameswaran * 4a1cba561SArun Parameswaran * This program is free software; you can redistribute it and/or 5a1cba561SArun Parameswaran * modify it under the terms of the GNU General Public License as 6a1cba561SArun Parameswaran * published by the Free Software Foundation version 2. 7a1cba561SArun Parameswaran * 8a1cba561SArun Parameswaran * This program is distributed "as is" WITHOUT ANY WARRANTY of any 9a1cba561SArun Parameswaran * kind, whether express or implied; without even the implied warranty 10a1cba561SArun Parameswaran * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11a1cba561SArun Parameswaran * GNU General Public License for more details. 12a1cba561SArun Parameswaran */ 13a1cba561SArun Parameswaran 14a1cba561SArun Parameswaran #include "bcm-phy-lib.h" 15a1cba561SArun Parameswaran #include <linux/brcmphy.h> 16a1cba561SArun Parameswaran #include <linux/export.h> 17a1cba561SArun Parameswaran #include <linux/mdio.h> 18b89eb1fcSArun Parameswaran #include <linux/module.h> 19a1cba561SArun Parameswaran #include <linux/phy.h> 20820ee17bSFlorian Fainelli #include <linux/ethtool.h> 21a1cba561SArun Parameswaran 22a1cba561SArun Parameswaran #define MII_BCM_CHANNEL_WIDTH 0x2000 23a1cba561SArun Parameswaran #define BCM_CL45VEN_EEE_ADV 0x3c 24a1cba561SArun Parameswaran 25a1cba561SArun Parameswaran int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 26a1cba561SArun Parameswaran { 27a1cba561SArun Parameswaran int rc; 28a1cba561SArun Parameswaran 29a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 30a1cba561SArun Parameswaran if (rc < 0) 31a1cba561SArun Parameswaran return rc; 32a1cba561SArun Parameswaran 33a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_EXP_DATA, val); 34a1cba561SArun Parameswaran } 35a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 36a1cba561SArun Parameswaran 37a1cba561SArun Parameswaran int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 38a1cba561SArun Parameswaran { 39a1cba561SArun Parameswaran int val; 40a1cba561SArun Parameswaran 41a1cba561SArun Parameswaran val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 42a1cba561SArun Parameswaran if (val < 0) 43a1cba561SArun Parameswaran return val; 44a1cba561SArun Parameswaran 45a1cba561SArun Parameswaran val = phy_read(phydev, MII_BCM54XX_EXP_DATA); 46a1cba561SArun Parameswaran 47a1cba561SArun Parameswaran /* Restore default value. It's O.K. if this write fails. */ 48a1cba561SArun Parameswaran phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); 49a1cba561SArun Parameswaran 50a1cba561SArun Parameswaran return val; 51a1cba561SArun Parameswaran } 52a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 53a1cba561SArun Parameswaran 545519da87SFlorian Fainelli int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) 555519da87SFlorian Fainelli { 565519da87SFlorian Fainelli /* The register must be written to both the Shadow Register Select and 575519da87SFlorian Fainelli * the Shadow Read Register Selector 585519da87SFlorian Fainelli */ 595519da87SFlorian Fainelli phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | 605519da87SFlorian Fainelli regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); 615519da87SFlorian Fainelli return phy_read(phydev, MII_BCM54XX_AUX_CTL); 625519da87SFlorian Fainelli } 635519da87SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 645519da87SFlorian Fainelli 655519da87SFlorian Fainelli int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 665519da87SFlorian Fainelli { 675519da87SFlorian Fainelli return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 685519da87SFlorian Fainelli } 695519da87SFlorian Fainelli EXPORT_SYMBOL(bcm54xx_auxctl_write); 705519da87SFlorian Fainelli 71a1cba561SArun Parameswaran int bcm_phy_write_misc(struct phy_device *phydev, 72a1cba561SArun Parameswaran u16 reg, u16 chl, u16 val) 73a1cba561SArun Parameswaran { 74a1cba561SArun Parameswaran int rc; 75a1cba561SArun Parameswaran int tmp; 76a1cba561SArun Parameswaran 77a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 78a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 79a1cba561SArun Parameswaran if (rc < 0) 80a1cba561SArun Parameswaran return rc; 81a1cba561SArun Parameswaran 82a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 83a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 84a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 85a1cba561SArun Parameswaran if (rc < 0) 86a1cba561SArun Parameswaran return rc; 87a1cba561SArun Parameswaran 88a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 89a1cba561SArun Parameswaran rc = bcm_phy_write_exp(phydev, tmp, val); 90a1cba561SArun Parameswaran 91a1cba561SArun Parameswaran return rc; 92a1cba561SArun Parameswaran } 93a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 94a1cba561SArun Parameswaran 95a1cba561SArun Parameswaran int bcm_phy_read_misc(struct phy_device *phydev, 96a1cba561SArun Parameswaran u16 reg, u16 chl) 97a1cba561SArun Parameswaran { 98a1cba561SArun Parameswaran int rc; 99a1cba561SArun Parameswaran int tmp; 100a1cba561SArun Parameswaran 101a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 102a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 103a1cba561SArun Parameswaran if (rc < 0) 104a1cba561SArun Parameswaran return rc; 105a1cba561SArun Parameswaran 106a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 107a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 108a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 109a1cba561SArun Parameswaran if (rc < 0) 110a1cba561SArun Parameswaran return rc; 111a1cba561SArun Parameswaran 112a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 113a1cba561SArun Parameswaran rc = bcm_phy_read_exp(phydev, tmp); 114a1cba561SArun Parameswaran 115a1cba561SArun Parameswaran return rc; 116a1cba561SArun Parameswaran } 117a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 118a1cba561SArun Parameswaran 119a1cba561SArun Parameswaran int bcm_phy_ack_intr(struct phy_device *phydev) 120a1cba561SArun Parameswaran { 121a1cba561SArun Parameswaran int reg; 122a1cba561SArun Parameswaran 123a1cba561SArun Parameswaran /* Clear pending interrupts. */ 124a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ISR); 125a1cba561SArun Parameswaran if (reg < 0) 126a1cba561SArun Parameswaran return reg; 127a1cba561SArun Parameswaran 128a1cba561SArun Parameswaran return 0; 129a1cba561SArun Parameswaran } 130a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 131a1cba561SArun Parameswaran 132a1cba561SArun Parameswaran int bcm_phy_config_intr(struct phy_device *phydev) 133a1cba561SArun Parameswaran { 134a1cba561SArun Parameswaran int reg; 135a1cba561SArun Parameswaran 136a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ECR); 137a1cba561SArun Parameswaran if (reg < 0) 138a1cba561SArun Parameswaran return reg; 139a1cba561SArun Parameswaran 140a1cba561SArun Parameswaran if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 141a1cba561SArun Parameswaran reg &= ~MII_BCM54XX_ECR_IM; 142a1cba561SArun Parameswaran else 143a1cba561SArun Parameswaran reg |= MII_BCM54XX_ECR_IM; 144a1cba561SArun Parameswaran 145a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_ECR, reg); 146a1cba561SArun Parameswaran } 147a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 148a1cba561SArun Parameswaran 149a1cba561SArun Parameswaran int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 150a1cba561SArun Parameswaran { 151a1cba561SArun Parameswaran phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 152a1cba561SArun Parameswaran return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 153a1cba561SArun Parameswaran } 154a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 155a1cba561SArun Parameswaran 156a1cba561SArun Parameswaran int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 157a1cba561SArun Parameswaran u16 val) 158a1cba561SArun Parameswaran { 159a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_SHD, 160a1cba561SArun Parameswaran MII_BCM54XX_SHD_WRITE | 161a1cba561SArun Parameswaran MII_BCM54XX_SHD_VAL(shadow) | 162a1cba561SArun Parameswaran MII_BCM54XX_SHD_DATA(val)); 163a1cba561SArun Parameswaran } 164a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 165a1cba561SArun Parameswaran 166a1cba561SArun Parameswaran int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 167a1cba561SArun Parameswaran { 168a1cba561SArun Parameswaran int val; 169a1cba561SArun Parameswaran 170a1cba561SArun Parameswaran if (dll_pwr_down) { 171a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 172a1cba561SArun Parameswaran if (val < 0) 173a1cba561SArun Parameswaran return val; 174a1cba561SArun Parameswaran 175a1cba561SArun Parameswaran val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 176a1cba561SArun Parameswaran bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 177a1cba561SArun Parameswaran } 178a1cba561SArun Parameswaran 179a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 180a1cba561SArun Parameswaran if (val < 0) 181a1cba561SArun Parameswaran return val; 182a1cba561SArun Parameswaran 183a1cba561SArun Parameswaran /* Clear APD bits */ 184a1cba561SArun Parameswaran val &= BCM_APD_CLR_MASK; 185a1cba561SArun Parameswaran 186a1cba561SArun Parameswaran if (phydev->autoneg == AUTONEG_ENABLE) 187a1cba561SArun Parameswaran val |= BCM54XX_SHD_APD_EN; 188a1cba561SArun Parameswaran else 189a1cba561SArun Parameswaran val |= BCM_NO_ANEG_APD_EN; 190a1cba561SArun Parameswaran 191a1cba561SArun Parameswaran /* Enable energy detect single link pulse for easy wakeup */ 192a1cba561SArun Parameswaran val |= BCM_APD_SINGLELP_EN; 193a1cba561SArun Parameswaran 194a1cba561SArun Parameswaran /* Enable Auto Power-Down (APD) for the PHY */ 195a1cba561SArun Parameswaran return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 196a1cba561SArun Parameswaran } 197a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 198a1cba561SArun Parameswaran 19999cec8a4SFlorian Fainelli int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 200a1cba561SArun Parameswaran { 201a1cba561SArun Parameswaran int val; 202a1cba561SArun Parameswaran 203a1cba561SArun Parameswaran /* Enable EEE at PHY level */ 204a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 205a1cba561SArun Parameswaran if (val < 0) 206a1cba561SArun Parameswaran return val; 207a1cba561SArun Parameswaran 20899cec8a4SFlorian Fainelli if (enable) 209a1cba561SArun Parameswaran val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 21099cec8a4SFlorian Fainelli else 21199cec8a4SFlorian Fainelli val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 212a1cba561SArun Parameswaran 213a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 214a1cba561SArun Parameswaran 215a1cba561SArun Parameswaran /* Advertise EEE */ 216a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 217a1cba561SArun Parameswaran if (val < 0) 218a1cba561SArun Parameswaran return val; 219a1cba561SArun Parameswaran 22099cec8a4SFlorian Fainelli if (enable) 221cda792c3SDoug Berger val |= (MDIO_EEE_100TX | MDIO_EEE_1000T); 22299cec8a4SFlorian Fainelli else 223cda792c3SDoug Berger val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); 224a1cba561SArun Parameswaran 225a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 226a1cba561SArun Parameswaran 227a1cba561SArun Parameswaran return 0; 228a1cba561SArun Parameswaran } 22999cec8a4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 230b89eb1fcSArun Parameswaran 231d06f78c4SFlorian Fainelli int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 232d06f78c4SFlorian Fainelli { 233d06f78c4SFlorian Fainelli int val; 234d06f78c4SFlorian Fainelli 235d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 236d06f78c4SFlorian Fainelli if (val < 0) 237d06f78c4SFlorian Fainelli return val; 238d06f78c4SFlorian Fainelli 239d06f78c4SFlorian Fainelli /* Check if wirespeed is enabled or not */ 240d06f78c4SFlorian Fainelli if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 241d06f78c4SFlorian Fainelli *count = DOWNSHIFT_DEV_DISABLE; 242d06f78c4SFlorian Fainelli return 0; 243d06f78c4SFlorian Fainelli } 244d06f78c4SFlorian Fainelli 245d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 246d06f78c4SFlorian Fainelli if (val < 0) 247d06f78c4SFlorian Fainelli return val; 248d06f78c4SFlorian Fainelli 249d06f78c4SFlorian Fainelli /* Downgrade after one link attempt */ 250d06f78c4SFlorian Fainelli if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 251d06f78c4SFlorian Fainelli *count = 1; 252d06f78c4SFlorian Fainelli } else { 253d06f78c4SFlorian Fainelli /* Downgrade after configured retry count */ 254d06f78c4SFlorian Fainelli val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 255d06f78c4SFlorian Fainelli val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 256d06f78c4SFlorian Fainelli *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 257d06f78c4SFlorian Fainelli } 258d06f78c4SFlorian Fainelli 259d06f78c4SFlorian Fainelli return 0; 260d06f78c4SFlorian Fainelli } 261d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 262d06f78c4SFlorian Fainelli 263d06f78c4SFlorian Fainelli int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 264d06f78c4SFlorian Fainelli { 265d06f78c4SFlorian Fainelli int val = 0, ret = 0; 266d06f78c4SFlorian Fainelli 267d06f78c4SFlorian Fainelli /* Range check the number given */ 268d06f78c4SFlorian Fainelli if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 269d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 270d06f78c4SFlorian Fainelli count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 271d06f78c4SFlorian Fainelli return -ERANGE; 272d06f78c4SFlorian Fainelli } 273d06f78c4SFlorian Fainelli 274d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 275d06f78c4SFlorian Fainelli if (val < 0) 276d06f78c4SFlorian Fainelli return val; 277d06f78c4SFlorian Fainelli 278d06f78c4SFlorian Fainelli /* Se the write enable bit */ 279d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_MISC_WREN; 280d06f78c4SFlorian Fainelli 281d06f78c4SFlorian Fainelli if (count == DOWNSHIFT_DEV_DISABLE) { 282d06f78c4SFlorian Fainelli val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 283d06f78c4SFlorian Fainelli return bcm54xx_auxctl_write(phydev, 284d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 285d06f78c4SFlorian Fainelli val); 286d06f78c4SFlorian Fainelli } else { 287d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 288d06f78c4SFlorian Fainelli ret = bcm54xx_auxctl_write(phydev, 289d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 290d06f78c4SFlorian Fainelli val); 291d06f78c4SFlorian Fainelli if (ret < 0) 292d06f78c4SFlorian Fainelli return ret; 293d06f78c4SFlorian Fainelli } 294d06f78c4SFlorian Fainelli 295d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 296d06f78c4SFlorian Fainelli val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 297d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 298d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 299d06f78c4SFlorian Fainelli 300d06f78c4SFlorian Fainelli switch (count) { 301d06f78c4SFlorian Fainelli case 1: 302d06f78c4SFlorian Fainelli val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 303d06f78c4SFlorian Fainelli break; 304d06f78c4SFlorian Fainelli case DOWNSHIFT_DEV_DEFAULT_COUNT: 305d06f78c4SFlorian Fainelli val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 306d06f78c4SFlorian Fainelli break; 307d06f78c4SFlorian Fainelli default: 308d06f78c4SFlorian Fainelli val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 309d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 310d06f78c4SFlorian Fainelli break; 311d06f78c4SFlorian Fainelli } 312d06f78c4SFlorian Fainelli 313d06f78c4SFlorian Fainelli return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 314d06f78c4SFlorian Fainelli } 315d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 316d06f78c4SFlorian Fainelli 317820ee17bSFlorian Fainelli struct bcm_phy_hw_stat { 318820ee17bSFlorian Fainelli const char *string; 319820ee17bSFlorian Fainelli u8 reg; 320820ee17bSFlorian Fainelli u8 shift; 321820ee17bSFlorian Fainelli u8 bits; 322820ee17bSFlorian Fainelli }; 323820ee17bSFlorian Fainelli 324820ee17bSFlorian Fainelli /* Counters freeze at either 0xffff or 0xff, better than nothing */ 325820ee17bSFlorian Fainelli static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 326820ee17bSFlorian Fainelli { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 327820ee17bSFlorian Fainelli { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 328820ee17bSFlorian Fainelli { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 329820ee17bSFlorian Fainelli { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 330820ee17bSFlorian Fainelli { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 331820ee17bSFlorian Fainelli }; 332820ee17bSFlorian Fainelli 333820ee17bSFlorian Fainelli int bcm_phy_get_sset_count(struct phy_device *phydev) 334820ee17bSFlorian Fainelli { 335820ee17bSFlorian Fainelli return ARRAY_SIZE(bcm_phy_hw_stats); 336820ee17bSFlorian Fainelli } 337820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 338820ee17bSFlorian Fainelli 339820ee17bSFlorian Fainelli void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 340820ee17bSFlorian Fainelli { 341820ee17bSFlorian Fainelli unsigned int i; 342820ee17bSFlorian Fainelli 343820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 344820ee17bSFlorian Fainelli memcpy(data + i * ETH_GSTRING_LEN, 345820ee17bSFlorian Fainelli bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 346820ee17bSFlorian Fainelli } 347820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 348820ee17bSFlorian Fainelli 349820ee17bSFlorian Fainelli #ifndef UINT64_MAX 350820ee17bSFlorian Fainelli #define UINT64_MAX (u64)(~((u64)0)) 351820ee17bSFlorian Fainelli #endif 352820ee17bSFlorian Fainelli 353820ee17bSFlorian Fainelli /* Caller is supposed to provide appropriate storage for the library code to 354820ee17bSFlorian Fainelli * access the shadow copy 355820ee17bSFlorian Fainelli */ 356820ee17bSFlorian Fainelli static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 357820ee17bSFlorian Fainelli unsigned int i) 358820ee17bSFlorian Fainelli { 359820ee17bSFlorian Fainelli struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 360820ee17bSFlorian Fainelli int val; 361820ee17bSFlorian Fainelli u64 ret; 362820ee17bSFlorian Fainelli 363820ee17bSFlorian Fainelli val = phy_read(phydev, stat.reg); 364820ee17bSFlorian Fainelli if (val < 0) { 365820ee17bSFlorian Fainelli ret = UINT64_MAX; 366820ee17bSFlorian Fainelli } else { 367820ee17bSFlorian Fainelli val >>= stat.shift; 368820ee17bSFlorian Fainelli val = val & ((1 << stat.bits) - 1); 369820ee17bSFlorian Fainelli shadow[i] += val; 370820ee17bSFlorian Fainelli ret = shadow[i]; 371820ee17bSFlorian Fainelli } 372820ee17bSFlorian Fainelli 373820ee17bSFlorian Fainelli return ret; 374820ee17bSFlorian Fainelli } 375820ee17bSFlorian Fainelli 376820ee17bSFlorian Fainelli void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 377820ee17bSFlorian Fainelli struct ethtool_stats *stats, u64 *data) 378820ee17bSFlorian Fainelli { 379820ee17bSFlorian Fainelli unsigned int i; 380820ee17bSFlorian Fainelli 381820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 382820ee17bSFlorian Fainelli data[i] = bcm_phy_get_stat(phydev, shadow, i); 383820ee17bSFlorian Fainelli } 384820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 385820ee17bSFlorian Fainelli 386b89eb1fcSArun Parameswaran MODULE_DESCRIPTION("Broadcom PHY Library"); 387b89eb1fcSArun Parameswaran MODULE_LICENSE("GPL v2"); 388b89eb1fcSArun Parameswaran MODULE_AUTHOR("Broadcom Corporation"); 389