1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0 2a1cba561SArun Parameswaran /* 3cda792c3SDoug Berger * Copyright (C) 2015-2017 Broadcom 4a1cba561SArun Parameswaran */ 5a1cba561SArun Parameswaran 6a1cba561SArun Parameswaran #include "bcm-phy-lib.h" 711ecf8c5SMichael Walle #include <linux/bitfield.h> 8a1cba561SArun Parameswaran #include <linux/brcmphy.h> 9a1cba561SArun Parameswaran #include <linux/export.h> 10a1cba561SArun Parameswaran #include <linux/mdio.h> 11b89eb1fcSArun Parameswaran #include <linux/module.h> 12a1cba561SArun Parameswaran #include <linux/phy.h> 13820ee17bSFlorian Fainelli #include <linux/ethtool.h> 1411ecf8c5SMichael Walle #include <linux/ethtool_netlink.h> 15a1cba561SArun Parameswaran 16a1cba561SArun Parameswaran #define MII_BCM_CHANNEL_WIDTH 0x2000 17a1cba561SArun Parameswaran #define BCM_CL45VEN_EEE_ADV 0x3c 18a1cba561SArun Parameswaran 197d7e7bceSMichael Walle int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 207d7e7bceSMichael Walle { 217d7e7bceSMichael Walle int rc; 227d7e7bceSMichael Walle 237d7e7bceSMichael Walle rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 247d7e7bceSMichael Walle if (rc < 0) 257d7e7bceSMichael Walle return rc; 267d7e7bceSMichael Walle 277d7e7bceSMichael Walle return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val); 287d7e7bceSMichael Walle } 297d7e7bceSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_write_exp); 307d7e7bceSMichael Walle 31a1cba561SArun Parameswaran int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 32a1cba561SArun Parameswaran { 33a1cba561SArun Parameswaran int rc; 34a1cba561SArun Parameswaran 357d7e7bceSMichael Walle phy_lock_mdio_bus(phydev); 367d7e7bceSMichael Walle rc = __bcm_phy_write_exp(phydev, reg, val); 377d7e7bceSMichael Walle phy_unlock_mdio_bus(phydev); 38a1cba561SArun Parameswaran 397d7e7bceSMichael Walle return rc; 40a1cba561SArun Parameswaran } 41a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 42a1cba561SArun Parameswaran 437d7e7bceSMichael Walle int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 44a1cba561SArun Parameswaran { 45a1cba561SArun Parameswaran int val; 46a1cba561SArun Parameswaran 477d7e7bceSMichael Walle val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 48a1cba561SArun Parameswaran if (val < 0) 49a1cba561SArun Parameswaran return val; 50a1cba561SArun Parameswaran 517d7e7bceSMichael Walle val = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 52a1cba561SArun Parameswaran 53a1cba561SArun Parameswaran /* Restore default value. It's O.K. if this write fails. */ 547d7e7bceSMichael Walle __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); 55a1cba561SArun Parameswaran 56a1cba561SArun Parameswaran return val; 57a1cba561SArun Parameswaran } 587d7e7bceSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_read_exp); 597d7e7bceSMichael Walle 607d7e7bceSMichael Walle int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 617d7e7bceSMichael Walle { 627d7e7bceSMichael Walle int rc; 637d7e7bceSMichael Walle 647d7e7bceSMichael Walle phy_lock_mdio_bus(phydev); 657d7e7bceSMichael Walle rc = __bcm_phy_read_exp(phydev, reg); 667d7e7bceSMichael Walle phy_unlock_mdio_bus(phydev); 677d7e7bceSMichael Walle 687d7e7bceSMichael Walle return rc; 697d7e7bceSMichael Walle } 70a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 71a1cba561SArun Parameswaran 72e184a907SMichael Walle int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 73e184a907SMichael Walle { 74e184a907SMichael Walle int new, ret; 75e184a907SMichael Walle 76e184a907SMichael Walle ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 77e184a907SMichael Walle if (ret < 0) 78e184a907SMichael Walle return ret; 79e184a907SMichael Walle 80e184a907SMichael Walle ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 81e184a907SMichael Walle if (ret < 0) 82e184a907SMichael Walle return ret; 83e184a907SMichael Walle 84e184a907SMichael Walle new = (ret & ~mask) | set; 85e184a907SMichael Walle if (new == ret) 86e184a907SMichael Walle return 0; 87e184a907SMichael Walle 88e184a907SMichael Walle return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new); 89e184a907SMichael Walle } 90e184a907SMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp); 91e184a907SMichael Walle 92e184a907SMichael Walle int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 93e184a907SMichael Walle { 94e184a907SMichael Walle int ret; 95e184a907SMichael Walle 96e184a907SMichael Walle phy_lock_mdio_bus(phydev); 97e184a907SMichael Walle ret = __bcm_phy_modify_exp(phydev, reg, mask, set); 98e184a907SMichael Walle phy_unlock_mdio_bus(phydev); 99e184a907SMichael Walle 100e184a907SMichael Walle return ret; 101e184a907SMichael Walle } 102e184a907SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_modify_exp); 103e184a907SMichael Walle 1045519da87SFlorian Fainelli int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) 1055519da87SFlorian Fainelli { 1065519da87SFlorian Fainelli /* The register must be written to both the Shadow Register Select and 1075519da87SFlorian Fainelli * the Shadow Read Register Selector 1085519da87SFlorian Fainelli */ 109733a969aSFlorian Fainelli phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK | 1105519da87SFlorian Fainelli regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); 1115519da87SFlorian Fainelli return phy_read(phydev, MII_BCM54XX_AUX_CTL); 1125519da87SFlorian Fainelli } 1135519da87SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 1145519da87SFlorian Fainelli 1155519da87SFlorian Fainelli int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 1165519da87SFlorian Fainelli { 1175519da87SFlorian Fainelli return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 1185519da87SFlorian Fainelli } 1195519da87SFlorian Fainelli EXPORT_SYMBOL(bcm54xx_auxctl_write); 1205519da87SFlorian Fainelli 121a1cba561SArun Parameswaran int bcm_phy_write_misc(struct phy_device *phydev, 122a1cba561SArun Parameswaran u16 reg, u16 chl, u16 val) 123a1cba561SArun Parameswaran { 124a1cba561SArun Parameswaran int rc; 125a1cba561SArun Parameswaran int tmp; 126a1cba561SArun Parameswaran 127a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 128a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 129a1cba561SArun Parameswaran if (rc < 0) 130a1cba561SArun Parameswaran return rc; 131a1cba561SArun Parameswaran 132a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 133a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 134a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 135a1cba561SArun Parameswaran if (rc < 0) 136a1cba561SArun Parameswaran return rc; 137a1cba561SArun Parameswaran 138a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 139a1cba561SArun Parameswaran rc = bcm_phy_write_exp(phydev, tmp, val); 140a1cba561SArun Parameswaran 141a1cba561SArun Parameswaran return rc; 142a1cba561SArun Parameswaran } 143a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 144a1cba561SArun Parameswaran 145a1cba561SArun Parameswaran int bcm_phy_read_misc(struct phy_device *phydev, 146a1cba561SArun Parameswaran u16 reg, u16 chl) 147a1cba561SArun Parameswaran { 148a1cba561SArun Parameswaran int rc; 149a1cba561SArun Parameswaran int tmp; 150a1cba561SArun Parameswaran 151a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 152a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 153a1cba561SArun Parameswaran if (rc < 0) 154a1cba561SArun Parameswaran return rc; 155a1cba561SArun Parameswaran 156a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 157a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 158a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 159a1cba561SArun Parameswaran if (rc < 0) 160a1cba561SArun Parameswaran return rc; 161a1cba561SArun Parameswaran 162a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 163a1cba561SArun Parameswaran rc = bcm_phy_read_exp(phydev, tmp); 164a1cba561SArun Parameswaran 165a1cba561SArun Parameswaran return rc; 166a1cba561SArun Parameswaran } 167a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 168a1cba561SArun Parameswaran 169a1cba561SArun Parameswaran int bcm_phy_ack_intr(struct phy_device *phydev) 170a1cba561SArun Parameswaran { 171a1cba561SArun Parameswaran int reg; 172a1cba561SArun Parameswaran 173a1cba561SArun Parameswaran /* Clear pending interrupts. */ 174a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ISR); 175a1cba561SArun Parameswaran if (reg < 0) 176a1cba561SArun Parameswaran return reg; 177a1cba561SArun Parameswaran 178a1cba561SArun Parameswaran return 0; 179a1cba561SArun Parameswaran } 180a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 181a1cba561SArun Parameswaran 182a1cba561SArun Parameswaran int bcm_phy_config_intr(struct phy_device *phydev) 183a1cba561SArun Parameswaran { 184a1cba561SArun Parameswaran int reg; 185a1cba561SArun Parameswaran 186a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ECR); 187a1cba561SArun Parameswaran if (reg < 0) 188a1cba561SArun Parameswaran return reg; 189a1cba561SArun Parameswaran 190a1cba561SArun Parameswaran if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 191a1cba561SArun Parameswaran reg &= ~MII_BCM54XX_ECR_IM; 192a1cba561SArun Parameswaran else 193a1cba561SArun Parameswaran reg |= MII_BCM54XX_ECR_IM; 194a1cba561SArun Parameswaran 195a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_ECR, reg); 196a1cba561SArun Parameswaran } 197a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 198a1cba561SArun Parameswaran 199a1cba561SArun Parameswaran int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 200a1cba561SArun Parameswaran { 201a1cba561SArun Parameswaran phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 202a1cba561SArun Parameswaran return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 203a1cba561SArun Parameswaran } 204a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 205a1cba561SArun Parameswaran 206a1cba561SArun Parameswaran int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 207a1cba561SArun Parameswaran u16 val) 208a1cba561SArun Parameswaran { 209a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_SHD, 210a1cba561SArun Parameswaran MII_BCM54XX_SHD_WRITE | 211a1cba561SArun Parameswaran MII_BCM54XX_SHD_VAL(shadow) | 212a1cba561SArun Parameswaran MII_BCM54XX_SHD_DATA(val)); 213a1cba561SArun Parameswaran } 214a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 215a1cba561SArun Parameswaran 2160a32f1ffSMichael Walle int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 2170a32f1ffSMichael Walle { 2180a32f1ffSMichael Walle int val; 2190a32f1ffSMichael Walle 2200a32f1ffSMichael Walle val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 2210a32f1ffSMichael Walle if (val < 0) 2220a32f1ffSMichael Walle return val; 2230a32f1ffSMichael Walle 2240a32f1ffSMichael Walle return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 2250a32f1ffSMichael Walle } 2260a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 2270a32f1ffSMichael Walle 2280a32f1ffSMichael Walle int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 2290a32f1ffSMichael Walle { 2300a32f1ffSMichael Walle int ret; 2310a32f1ffSMichael Walle 2320a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 2330a32f1ffSMichael Walle ret = __bcm_phy_read_rdb(phydev, rdb); 2340a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 2350a32f1ffSMichael Walle 2360a32f1ffSMichael Walle return ret; 2370a32f1ffSMichael Walle } 2380a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 2390a32f1ffSMichael Walle 2400a32f1ffSMichael Walle int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 2410a32f1ffSMichael Walle { 2420a32f1ffSMichael Walle int ret; 2430a32f1ffSMichael Walle 2440a32f1ffSMichael Walle ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 2450a32f1ffSMichael Walle if (ret < 0) 2460a32f1ffSMichael Walle return ret; 2470a32f1ffSMichael Walle 2480a32f1ffSMichael Walle return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 2490a32f1ffSMichael Walle } 2500a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 2510a32f1ffSMichael Walle 2520a32f1ffSMichael Walle int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 2530a32f1ffSMichael Walle { 2540a32f1ffSMichael Walle int ret; 2550a32f1ffSMichael Walle 2560a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 2570a32f1ffSMichael Walle ret = __bcm_phy_write_rdb(phydev, rdb, val); 2580a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 2590a32f1ffSMichael Walle 2600a32f1ffSMichael Walle return ret; 2610a32f1ffSMichael Walle } 2620a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 2630a32f1ffSMichael Walle 2640a32f1ffSMichael Walle int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 2650a32f1ffSMichael Walle { 2660a32f1ffSMichael Walle int new, ret; 2670a32f1ffSMichael Walle 2680a32f1ffSMichael Walle ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 2690a32f1ffSMichael Walle if (ret < 0) 2700a32f1ffSMichael Walle return ret; 2710a32f1ffSMichael Walle 2720a32f1ffSMichael Walle ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 2730a32f1ffSMichael Walle if (ret < 0) 2740a32f1ffSMichael Walle return ret; 2750a32f1ffSMichael Walle 2760a32f1ffSMichael Walle new = (ret & ~mask) | set; 2770a32f1ffSMichael Walle if (new == ret) 2780a32f1ffSMichael Walle return 0; 2790a32f1ffSMichael Walle 2800a32f1ffSMichael Walle return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 2810a32f1ffSMichael Walle } 2820a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 2830a32f1ffSMichael Walle 2840a32f1ffSMichael Walle int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 2850a32f1ffSMichael Walle { 2860a32f1ffSMichael Walle int ret; 2870a32f1ffSMichael Walle 2880a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 2890a32f1ffSMichael Walle ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 2900a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 2910a32f1ffSMichael Walle 2920a32f1ffSMichael Walle return ret; 2930a32f1ffSMichael Walle } 2940a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 2950a32f1ffSMichael Walle 296a1cba561SArun Parameswaran int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 297a1cba561SArun Parameswaran { 298a1cba561SArun Parameswaran int val; 299a1cba561SArun Parameswaran 300a1cba561SArun Parameswaran if (dll_pwr_down) { 301a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 302a1cba561SArun Parameswaran if (val < 0) 303a1cba561SArun Parameswaran return val; 304a1cba561SArun Parameswaran 305a1cba561SArun Parameswaran val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 306a1cba561SArun Parameswaran bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 307a1cba561SArun Parameswaran } 308a1cba561SArun Parameswaran 309a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 310a1cba561SArun Parameswaran if (val < 0) 311a1cba561SArun Parameswaran return val; 312a1cba561SArun Parameswaran 313a1cba561SArun Parameswaran /* Clear APD bits */ 314a1cba561SArun Parameswaran val &= BCM_APD_CLR_MASK; 315a1cba561SArun Parameswaran 316a1cba561SArun Parameswaran if (phydev->autoneg == AUTONEG_ENABLE) 317a1cba561SArun Parameswaran val |= BCM54XX_SHD_APD_EN; 318a1cba561SArun Parameswaran else 319a1cba561SArun Parameswaran val |= BCM_NO_ANEG_APD_EN; 320a1cba561SArun Parameswaran 321a1cba561SArun Parameswaran /* Enable energy detect single link pulse for easy wakeup */ 322a1cba561SArun Parameswaran val |= BCM_APD_SINGLELP_EN; 323a1cba561SArun Parameswaran 324a1cba561SArun Parameswaran /* Enable Auto Power-Down (APD) for the PHY */ 325a1cba561SArun Parameswaran return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 326a1cba561SArun Parameswaran } 327a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 328a1cba561SArun Parameswaran 32999cec8a4SFlorian Fainelli int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 330a1cba561SArun Parameswaran { 331a1cba561SArun Parameswaran int val; 332a1cba561SArun Parameswaran 333a1cba561SArun Parameswaran /* Enable EEE at PHY level */ 334a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 335a1cba561SArun Parameswaran if (val < 0) 336a1cba561SArun Parameswaran return val; 337a1cba561SArun Parameswaran 33899cec8a4SFlorian Fainelli if (enable) 339a1cba561SArun Parameswaran val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 34099cec8a4SFlorian Fainelli else 34199cec8a4SFlorian Fainelli val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 342a1cba561SArun Parameswaran 343a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 344a1cba561SArun Parameswaran 345a1cba561SArun Parameswaran /* Advertise EEE */ 346a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 347a1cba561SArun Parameswaran if (val < 0) 348a1cba561SArun Parameswaran return val; 349a1cba561SArun Parameswaran 35099cec8a4SFlorian Fainelli if (enable) 351cda792c3SDoug Berger val |= (MDIO_EEE_100TX | MDIO_EEE_1000T); 35299cec8a4SFlorian Fainelli else 353cda792c3SDoug Berger val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); 354a1cba561SArun Parameswaran 355a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 356a1cba561SArun Parameswaran 357a1cba561SArun Parameswaran return 0; 358a1cba561SArun Parameswaran } 35999cec8a4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 360b89eb1fcSArun Parameswaran 361d06f78c4SFlorian Fainelli int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 362d06f78c4SFlorian Fainelli { 363d06f78c4SFlorian Fainelli int val; 364d06f78c4SFlorian Fainelli 365d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 366d06f78c4SFlorian Fainelli if (val < 0) 367d06f78c4SFlorian Fainelli return val; 368d06f78c4SFlorian Fainelli 369d06f78c4SFlorian Fainelli /* Check if wirespeed is enabled or not */ 370d06f78c4SFlorian Fainelli if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 371d06f78c4SFlorian Fainelli *count = DOWNSHIFT_DEV_DISABLE; 372d06f78c4SFlorian Fainelli return 0; 373d06f78c4SFlorian Fainelli } 374d06f78c4SFlorian Fainelli 375d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 376d06f78c4SFlorian Fainelli if (val < 0) 377d06f78c4SFlorian Fainelli return val; 378d06f78c4SFlorian Fainelli 379d06f78c4SFlorian Fainelli /* Downgrade after one link attempt */ 380d06f78c4SFlorian Fainelli if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 381d06f78c4SFlorian Fainelli *count = 1; 382d06f78c4SFlorian Fainelli } else { 383d06f78c4SFlorian Fainelli /* Downgrade after configured retry count */ 384d06f78c4SFlorian Fainelli val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 385d06f78c4SFlorian Fainelli val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 386d06f78c4SFlorian Fainelli *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 387d06f78c4SFlorian Fainelli } 388d06f78c4SFlorian Fainelli 389d06f78c4SFlorian Fainelli return 0; 390d06f78c4SFlorian Fainelli } 391d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 392d06f78c4SFlorian Fainelli 393d06f78c4SFlorian Fainelli int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 394d06f78c4SFlorian Fainelli { 395d06f78c4SFlorian Fainelli int val = 0, ret = 0; 396d06f78c4SFlorian Fainelli 397d06f78c4SFlorian Fainelli /* Range check the number given */ 398d06f78c4SFlorian Fainelli if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 399d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 400d06f78c4SFlorian Fainelli count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 401d06f78c4SFlorian Fainelli return -ERANGE; 402d06f78c4SFlorian Fainelli } 403d06f78c4SFlorian Fainelli 404d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 405d06f78c4SFlorian Fainelli if (val < 0) 406d06f78c4SFlorian Fainelli return val; 407d06f78c4SFlorian Fainelli 408d06f78c4SFlorian Fainelli /* Se the write enable bit */ 409d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_MISC_WREN; 410d06f78c4SFlorian Fainelli 411d06f78c4SFlorian Fainelli if (count == DOWNSHIFT_DEV_DISABLE) { 412d06f78c4SFlorian Fainelli val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 413d06f78c4SFlorian Fainelli return bcm54xx_auxctl_write(phydev, 414d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 415d06f78c4SFlorian Fainelli val); 416d06f78c4SFlorian Fainelli } else { 417d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 418d06f78c4SFlorian Fainelli ret = bcm54xx_auxctl_write(phydev, 419d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 420d06f78c4SFlorian Fainelli val); 421d06f78c4SFlorian Fainelli if (ret < 0) 422d06f78c4SFlorian Fainelli return ret; 423d06f78c4SFlorian Fainelli } 424d06f78c4SFlorian Fainelli 425d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 426d06f78c4SFlorian Fainelli val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 427d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 428d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 429d06f78c4SFlorian Fainelli 430d06f78c4SFlorian Fainelli switch (count) { 431d06f78c4SFlorian Fainelli case 1: 432d06f78c4SFlorian Fainelli val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 433d06f78c4SFlorian Fainelli break; 434d06f78c4SFlorian Fainelli case DOWNSHIFT_DEV_DEFAULT_COUNT: 435d06f78c4SFlorian Fainelli val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 436d06f78c4SFlorian Fainelli break; 437d06f78c4SFlorian Fainelli default: 438d06f78c4SFlorian Fainelli val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 439d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 440d06f78c4SFlorian Fainelli break; 441d06f78c4SFlorian Fainelli } 442d06f78c4SFlorian Fainelli 443d06f78c4SFlorian Fainelli return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 444d06f78c4SFlorian Fainelli } 445d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 446d06f78c4SFlorian Fainelli 447820ee17bSFlorian Fainelli struct bcm_phy_hw_stat { 448820ee17bSFlorian Fainelli const char *string; 449820ee17bSFlorian Fainelli u8 reg; 450820ee17bSFlorian Fainelli u8 shift; 451820ee17bSFlorian Fainelli u8 bits; 452820ee17bSFlorian Fainelli }; 453820ee17bSFlorian Fainelli 454820ee17bSFlorian Fainelli /* Counters freeze at either 0xffff or 0xff, better than nothing */ 455820ee17bSFlorian Fainelli static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 456820ee17bSFlorian Fainelli { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 457820ee17bSFlorian Fainelli { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 458820ee17bSFlorian Fainelli { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 459820ee17bSFlorian Fainelli { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 460820ee17bSFlorian Fainelli { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 461820ee17bSFlorian Fainelli }; 462820ee17bSFlorian Fainelli 463820ee17bSFlorian Fainelli int bcm_phy_get_sset_count(struct phy_device *phydev) 464820ee17bSFlorian Fainelli { 465820ee17bSFlorian Fainelli return ARRAY_SIZE(bcm_phy_hw_stats); 466820ee17bSFlorian Fainelli } 467820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 468820ee17bSFlorian Fainelli 469820ee17bSFlorian Fainelli void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 470820ee17bSFlorian Fainelli { 471820ee17bSFlorian Fainelli unsigned int i; 472820ee17bSFlorian Fainelli 473820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 4748a17eefaSFlorian Fainelli strlcpy(data + i * ETH_GSTRING_LEN, 475820ee17bSFlorian Fainelli bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 476820ee17bSFlorian Fainelli } 477820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 478820ee17bSFlorian Fainelli 479820ee17bSFlorian Fainelli /* Caller is supposed to provide appropriate storage for the library code to 480820ee17bSFlorian Fainelli * access the shadow copy 481820ee17bSFlorian Fainelli */ 482820ee17bSFlorian Fainelli static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 483820ee17bSFlorian Fainelli unsigned int i) 484820ee17bSFlorian Fainelli { 485820ee17bSFlorian Fainelli struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 486820ee17bSFlorian Fainelli int val; 487820ee17bSFlorian Fainelli u64 ret; 488820ee17bSFlorian Fainelli 489820ee17bSFlorian Fainelli val = phy_read(phydev, stat.reg); 490820ee17bSFlorian Fainelli if (val < 0) { 4916c3442f5SJisheng Zhang ret = U64_MAX; 492820ee17bSFlorian Fainelli } else { 493820ee17bSFlorian Fainelli val >>= stat.shift; 494820ee17bSFlorian Fainelli val = val & ((1 << stat.bits) - 1); 495820ee17bSFlorian Fainelli shadow[i] += val; 496820ee17bSFlorian Fainelli ret = shadow[i]; 497820ee17bSFlorian Fainelli } 498820ee17bSFlorian Fainelli 499820ee17bSFlorian Fainelli return ret; 500820ee17bSFlorian Fainelli } 501820ee17bSFlorian Fainelli 502820ee17bSFlorian Fainelli void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 503820ee17bSFlorian Fainelli struct ethtool_stats *stats, u64 *data) 504820ee17bSFlorian Fainelli { 505820ee17bSFlorian Fainelli unsigned int i; 506820ee17bSFlorian Fainelli 507820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 508820ee17bSFlorian Fainelli data[i] = bcm_phy_get_stat(phydev, shadow, i); 509820ee17bSFlorian Fainelli } 510820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 511820ee17bSFlorian Fainelli 512f878fe56SFlorian Fainelli void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 513f878fe56SFlorian Fainelli { 514f878fe56SFlorian Fainelli /* Reset R_CAL/RC_CAL Engine */ 515f878fe56SFlorian Fainelli bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 516f878fe56SFlorian Fainelli 517f878fe56SFlorian Fainelli /* Disable Reset R_AL/RC_CAL Engine */ 518f878fe56SFlorian Fainelli bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 519f878fe56SFlorian Fainelli } 520f878fe56SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 521f878fe56SFlorian Fainelli 522f878fe56SFlorian Fainelli int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 523f878fe56SFlorian Fainelli { 524f878fe56SFlorian Fainelli /* Increase VCO range to prevent unlocking problem of PLL at low 525f878fe56SFlorian Fainelli * temp 526f878fe56SFlorian Fainelli */ 527f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 528f878fe56SFlorian Fainelli 529f878fe56SFlorian Fainelli /* Change Ki to 011 */ 530f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 531f878fe56SFlorian Fainelli 532f878fe56SFlorian Fainelli /* Disable loading of TVCO buffer to bandgap, set bandgap trim 533f878fe56SFlorian Fainelli * to 111 534f878fe56SFlorian Fainelli */ 535f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 536f878fe56SFlorian Fainelli 537f878fe56SFlorian Fainelli /* Adjust bias current trim by -3 */ 538f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 539f878fe56SFlorian Fainelli 540f878fe56SFlorian Fainelli /* Switch to CORE_BASE1E */ 541f878fe56SFlorian Fainelli phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 542f878fe56SFlorian Fainelli 543f878fe56SFlorian Fainelli bcm_phy_r_rc_cal_reset(phydev); 544f878fe56SFlorian Fainelli 545f878fe56SFlorian Fainelli /* write AFE_RXCONFIG_0 */ 546f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 547f878fe56SFlorian Fainelli 548f878fe56SFlorian Fainelli /* write AFE_RXCONFIG_1 */ 549f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 550f878fe56SFlorian Fainelli 551f878fe56SFlorian Fainelli /* write AFE_RX_LP_COUNTER */ 552f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 553f878fe56SFlorian Fainelli 554f878fe56SFlorian Fainelli /* write AFE_HPF_TRIM_OTHERS */ 555f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 556f878fe56SFlorian Fainelli 557f878fe56SFlorian Fainelli /* write AFTE_TX_CONFIG */ 558f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 559f878fe56SFlorian Fainelli 560f878fe56SFlorian Fainelli return 0; 561f878fe56SFlorian Fainelli } 562f878fe56SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 563f878fe56SFlorian Fainelli 564ab41ca34SMurali Krishna Policharla int bcm_phy_enable_jumbo(struct phy_device *phydev) 565ab41ca34SMurali Krishna Policharla { 566ab41ca34SMurali Krishna Policharla int ret; 567ab41ca34SMurali Krishna Policharla 568ab41ca34SMurali Krishna Policharla ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 569ab41ca34SMurali Krishna Policharla if (ret < 0) 570ab41ca34SMurali Krishna Policharla return ret; 571ab41ca34SMurali Krishna Policharla 572ab41ca34SMurali Krishna Policharla /* Enable extended length packet reception */ 573ab41ca34SMurali Krishna Policharla ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 574ab41ca34SMurali Krishna Policharla ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 575ab41ca34SMurali Krishna Policharla if (ret < 0) 576ab41ca34SMurali Krishna Policharla return ret; 577ab41ca34SMurali Krishna Policharla 578ab41ca34SMurali Krishna Policharla /* Enable the elastic FIFO for raising the transmission limit from 579ab41ca34SMurali Krishna Policharla * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 580ab41ca34SMurali Krishna Policharla * latency. 581ab41ca34SMurali Krishna Policharla */ 582ab41ca34SMurali Krishna Policharla return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 583ab41ca34SMurali Krishna Policharla } 584ab41ca34SMurali Krishna Policharla EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 585ab41ca34SMurali Krishna Policharla 58611ecf8c5SMichael Walle int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 58711ecf8c5SMichael Walle { 58811ecf8c5SMichael Walle return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 58911ecf8c5SMichael Walle } 59011ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_enable_rdb_access); 59111ecf8c5SMichael Walle 59211ecf8c5SMichael Walle int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 59311ecf8c5SMichael Walle { 59411ecf8c5SMichael Walle return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 59511ecf8c5SMichael Walle BCM54XX_ACCESS_MODE_LEGACY_EN); 59611ecf8c5SMichael Walle } 59711ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_enable_legacy_access); 59811ecf8c5SMichael Walle 59911ecf8c5SMichael Walle static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 60011ecf8c5SMichael Walle { 60111ecf8c5SMichael Walle u16 mask, set; 60211ecf8c5SMichael Walle int ret; 60311ecf8c5SMichael Walle 60411ecf8c5SMichael Walle /* Auto-negotiation must be enabled for cable diagnostics to work, but 60511ecf8c5SMichael Walle * don't advertise any capabilities. 60611ecf8c5SMichael Walle */ 60711ecf8c5SMichael Walle phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 60811ecf8c5SMichael Walle phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 60911ecf8c5SMichael Walle phy_write(phydev, MII_CTRL1000, 0); 61011ecf8c5SMichael Walle 61111ecf8c5SMichael Walle phy_lock_mdio_bus(phydev); 61211ecf8c5SMichael Walle if (is_rdb) { 61311ecf8c5SMichael Walle ret = __bcm_phy_enable_legacy_access(phydev); 61411ecf8c5SMichael Walle if (ret) 61511ecf8c5SMichael Walle goto out; 61611ecf8c5SMichael Walle } 61711ecf8c5SMichael Walle 61811ecf8c5SMichael Walle mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 61911ecf8c5SMichael Walle set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 62011ecf8c5SMichael Walle FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 62111ecf8c5SMichael Walle BCM54XX_ECD_CTRL_UNIT_CM); 62211ecf8c5SMichael Walle 62311ecf8c5SMichael Walle ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 62411ecf8c5SMichael Walle 62511ecf8c5SMichael Walle out: 62611ecf8c5SMichael Walle /* re-enable the RDB access even if there was an error */ 62711ecf8c5SMichael Walle if (is_rdb) 62811ecf8c5SMichael Walle ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 62911ecf8c5SMichael Walle 63011ecf8c5SMichael Walle phy_unlock_mdio_bus(phydev); 63111ecf8c5SMichael Walle 63211ecf8c5SMichael Walle return ret; 63311ecf8c5SMichael Walle } 63411ecf8c5SMichael Walle 63511ecf8c5SMichael Walle static int bcm_phy_cable_test_report_trans(int result) 63611ecf8c5SMichael Walle { 63711ecf8c5SMichael Walle switch (result) { 63811ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OK: 63911ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_OK; 64011ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OPEN: 64111ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 64211ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 64311ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 64411ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 64511ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 64611ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_INVALID: 64711ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_BUSY: 64811ecf8c5SMichael Walle default: 64911ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 65011ecf8c5SMichael Walle } 65111ecf8c5SMichael Walle } 65211ecf8c5SMichael Walle 65311ecf8c5SMichael Walle static bool bcm_phy_distance_valid(int result) 65411ecf8c5SMichael Walle { 65511ecf8c5SMichael Walle switch (result) { 65611ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OPEN: 65711ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 65811ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 65911ecf8c5SMichael Walle return true; 66011ecf8c5SMichael Walle } 66111ecf8c5SMichael Walle return false; 66211ecf8c5SMichael Walle } 66311ecf8c5SMichael Walle 66411ecf8c5SMichael Walle static int bcm_phy_report_length(struct phy_device *phydev, int pair) 66511ecf8c5SMichael Walle { 66611ecf8c5SMichael Walle int val; 66711ecf8c5SMichael Walle 66811ecf8c5SMichael Walle val = __bcm_phy_read_exp(phydev, 66911ecf8c5SMichael Walle BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 67011ecf8c5SMichael Walle if (val < 0) 67111ecf8c5SMichael Walle return val; 67211ecf8c5SMichael Walle 67311ecf8c5SMichael Walle if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 67411ecf8c5SMichael Walle return 0; 67511ecf8c5SMichael Walle 67611ecf8c5SMichael Walle ethnl_cable_test_fault_length(phydev, pair, val); 67711ecf8c5SMichael Walle 67811ecf8c5SMichael Walle return 0; 67911ecf8c5SMichael Walle } 68011ecf8c5SMichael Walle 68111ecf8c5SMichael Walle static int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 68211ecf8c5SMichael Walle bool *finished, bool is_rdb) 68311ecf8c5SMichael Walle { 68411ecf8c5SMichael Walle int pair_a, pair_b, pair_c, pair_d, ret; 68511ecf8c5SMichael Walle 68611ecf8c5SMichael Walle *finished = false; 68711ecf8c5SMichael Walle 68811ecf8c5SMichael Walle phy_lock_mdio_bus(phydev); 68911ecf8c5SMichael Walle 69011ecf8c5SMichael Walle if (is_rdb) { 69111ecf8c5SMichael Walle ret = __bcm_phy_enable_legacy_access(phydev); 69211ecf8c5SMichael Walle if (ret) 69311ecf8c5SMichael Walle goto out; 69411ecf8c5SMichael Walle } 69511ecf8c5SMichael Walle 69611ecf8c5SMichael Walle ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 69711ecf8c5SMichael Walle if (ret < 0) 69811ecf8c5SMichael Walle goto out; 69911ecf8c5SMichael Walle 70011ecf8c5SMichael Walle if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 70111ecf8c5SMichael Walle ret = 0; 70211ecf8c5SMichael Walle goto out; 70311ecf8c5SMichael Walle } 70411ecf8c5SMichael Walle 70511ecf8c5SMichael Walle ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 70611ecf8c5SMichael Walle if (ret < 0) 70711ecf8c5SMichael Walle goto out; 70811ecf8c5SMichael Walle 70911ecf8c5SMichael Walle pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 71011ecf8c5SMichael Walle pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 71111ecf8c5SMichael Walle pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 71211ecf8c5SMichael Walle pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 71311ecf8c5SMichael Walle 71411ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 71511ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_a)); 71611ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 71711ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_b)); 71811ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 71911ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_c)); 72011ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 72111ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_d)); 72211ecf8c5SMichael Walle 72311ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_a)) 72411ecf8c5SMichael Walle bcm_phy_report_length(phydev, 0); 72511ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_b)) 72611ecf8c5SMichael Walle bcm_phy_report_length(phydev, 1); 72711ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_c)) 72811ecf8c5SMichael Walle bcm_phy_report_length(phydev, 2); 72911ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_d)) 73011ecf8c5SMichael Walle bcm_phy_report_length(phydev, 3); 73111ecf8c5SMichael Walle 73211ecf8c5SMichael Walle ret = 0; 73311ecf8c5SMichael Walle *finished = true; 73411ecf8c5SMichael Walle out: 73511ecf8c5SMichael Walle /* re-enable the RDB access even if there was an error */ 73611ecf8c5SMichael Walle if (is_rdb) 73711ecf8c5SMichael Walle ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 73811ecf8c5SMichael Walle 73911ecf8c5SMichael Walle phy_unlock_mdio_bus(phydev); 74011ecf8c5SMichael Walle 74111ecf8c5SMichael Walle return ret; 74211ecf8c5SMichael Walle } 74311ecf8c5SMichael Walle 74411ecf8c5SMichael Walle int bcm_phy_cable_test_start(struct phy_device *phydev) 74511ecf8c5SMichael Walle { 74611ecf8c5SMichael Walle return _bcm_phy_cable_test_start(phydev, false); 74711ecf8c5SMichael Walle } 74811ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 74911ecf8c5SMichael Walle 75011ecf8c5SMichael Walle int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 75111ecf8c5SMichael Walle { 75211ecf8c5SMichael Walle return _bcm_phy_cable_test_get_status(phydev, finished, false); 75311ecf8c5SMichael Walle } 75411ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 75511ecf8c5SMichael Walle 75611ecf8c5SMichael Walle /* We assume that all PHYs which support RDB access can be switched to legacy 75711ecf8c5SMichael Walle * mode. If, in the future, this is not true anymore, we have to re-implement 75811ecf8c5SMichael Walle * this with RDB access. 75911ecf8c5SMichael Walle */ 76011ecf8c5SMichael Walle int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 76111ecf8c5SMichael Walle { 76211ecf8c5SMichael Walle return _bcm_phy_cable_test_start(phydev, true); 76311ecf8c5SMichael Walle } 76411ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 76511ecf8c5SMichael Walle 76611ecf8c5SMichael Walle int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 76711ecf8c5SMichael Walle bool *finished) 76811ecf8c5SMichael Walle { 76911ecf8c5SMichael Walle return _bcm_phy_cable_test_get_status(phydev, finished, true); 77011ecf8c5SMichael Walle } 77111ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 77211ecf8c5SMichael Walle 773b89eb1fcSArun Parameswaran MODULE_DESCRIPTION("Broadcom PHY Library"); 774b89eb1fcSArun Parameswaran MODULE_LICENSE("GPL v2"); 775b89eb1fcSArun Parameswaran MODULE_AUTHOR("Broadcom Corporation"); 776