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> 98baddaa9SFlorian Fainelli #include <linux/etherdevice.h> 10a1cba561SArun Parameswaran #include <linux/export.h> 11a1cba561SArun Parameswaran #include <linux/mdio.h> 12b89eb1fcSArun Parameswaran #include <linux/module.h> 13a1cba561SArun Parameswaran #include <linux/phy.h> 14820ee17bSFlorian Fainelli #include <linux/ethtool.h> 1511ecf8c5SMichael Walle #include <linux/ethtool_netlink.h> 168baddaa9SFlorian Fainelli #include <linux/netdevice.h> 17a1cba561SArun Parameswaran 18a1cba561SArun Parameswaran #define MII_BCM_CHANNEL_WIDTH 0x2000 19a1cba561SArun Parameswaran #define BCM_CL45VEN_EEE_ADV 0x3c 20a1cba561SArun Parameswaran 217d7e7bceSMichael Walle int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 227d7e7bceSMichael Walle { 237d7e7bceSMichael Walle int rc; 247d7e7bceSMichael Walle 257d7e7bceSMichael Walle rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 267d7e7bceSMichael Walle if (rc < 0) 277d7e7bceSMichael Walle return rc; 287d7e7bceSMichael Walle 297d7e7bceSMichael Walle return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val); 307d7e7bceSMichael Walle } 317d7e7bceSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_write_exp); 327d7e7bceSMichael Walle 33a1cba561SArun Parameswaran int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 34a1cba561SArun Parameswaran { 35a1cba561SArun Parameswaran int rc; 36a1cba561SArun Parameswaran 377d7e7bceSMichael Walle phy_lock_mdio_bus(phydev); 387d7e7bceSMichael Walle rc = __bcm_phy_write_exp(phydev, reg, val); 397d7e7bceSMichael Walle phy_unlock_mdio_bus(phydev); 40a1cba561SArun Parameswaran 417d7e7bceSMichael Walle return rc; 42a1cba561SArun Parameswaran } 43a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 44a1cba561SArun Parameswaran 457d7e7bceSMichael Walle int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 46a1cba561SArun Parameswaran { 47a1cba561SArun Parameswaran int val; 48a1cba561SArun Parameswaran 497d7e7bceSMichael Walle val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 50a1cba561SArun Parameswaran if (val < 0) 51a1cba561SArun Parameswaran return val; 52a1cba561SArun Parameswaran 537d7e7bceSMichael Walle val = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 54a1cba561SArun Parameswaran 55a1cba561SArun Parameswaran /* Restore default value. It's O.K. if this write fails. */ 567d7e7bceSMichael Walle __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); 57a1cba561SArun Parameswaran 58a1cba561SArun Parameswaran return val; 59a1cba561SArun Parameswaran } 607d7e7bceSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_read_exp); 617d7e7bceSMichael Walle 627d7e7bceSMichael Walle int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 637d7e7bceSMichael Walle { 647d7e7bceSMichael Walle int rc; 657d7e7bceSMichael Walle 667d7e7bceSMichael Walle phy_lock_mdio_bus(phydev); 677d7e7bceSMichael Walle rc = __bcm_phy_read_exp(phydev, reg); 687d7e7bceSMichael Walle phy_unlock_mdio_bus(phydev); 697d7e7bceSMichael Walle 707d7e7bceSMichael Walle return rc; 717d7e7bceSMichael Walle } 72a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 73a1cba561SArun Parameswaran 74e184a907SMichael Walle int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 75e184a907SMichael Walle { 76e184a907SMichael Walle int new, ret; 77e184a907SMichael Walle 78e184a907SMichael Walle ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 79e184a907SMichael Walle if (ret < 0) 80e184a907SMichael Walle return ret; 81e184a907SMichael Walle 82e184a907SMichael Walle ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 83e184a907SMichael Walle if (ret < 0) 84e184a907SMichael Walle return ret; 85e184a907SMichael Walle 86e184a907SMichael Walle new = (ret & ~mask) | set; 87e184a907SMichael Walle if (new == ret) 88e184a907SMichael Walle return 0; 89e184a907SMichael Walle 90e184a907SMichael Walle return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new); 91e184a907SMichael Walle } 92e184a907SMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp); 93e184a907SMichael Walle 94e184a907SMichael Walle int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 95e184a907SMichael Walle { 96e184a907SMichael Walle int ret; 97e184a907SMichael Walle 98e184a907SMichael Walle phy_lock_mdio_bus(phydev); 99e184a907SMichael Walle ret = __bcm_phy_modify_exp(phydev, reg, mask, set); 100e184a907SMichael Walle phy_unlock_mdio_bus(phydev); 101e184a907SMichael Walle 102e184a907SMichael Walle return ret; 103e184a907SMichael Walle } 104e184a907SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_modify_exp); 105e184a907SMichael Walle 1065519da87SFlorian Fainelli int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) 1075519da87SFlorian Fainelli { 1085519da87SFlorian Fainelli /* The register must be written to both the Shadow Register Select and 1095519da87SFlorian Fainelli * the Shadow Read Register Selector 1105519da87SFlorian Fainelli */ 111733a969aSFlorian Fainelli phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK | 1125519da87SFlorian Fainelli regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); 1135519da87SFlorian Fainelli return phy_read(phydev, MII_BCM54XX_AUX_CTL); 1145519da87SFlorian Fainelli } 1155519da87SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 1165519da87SFlorian Fainelli 1175519da87SFlorian Fainelli int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 1185519da87SFlorian Fainelli { 1195519da87SFlorian Fainelli return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 1205519da87SFlorian Fainelli } 1215519da87SFlorian Fainelli EXPORT_SYMBOL(bcm54xx_auxctl_write); 1225519da87SFlorian Fainelli 123a1cba561SArun Parameswaran int bcm_phy_write_misc(struct phy_device *phydev, 124a1cba561SArun Parameswaran u16 reg, u16 chl, u16 val) 125a1cba561SArun Parameswaran { 126a1cba561SArun Parameswaran int rc; 127a1cba561SArun Parameswaran int tmp; 128a1cba561SArun Parameswaran 129a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 130a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 131a1cba561SArun Parameswaran if (rc < 0) 132a1cba561SArun Parameswaran return rc; 133a1cba561SArun Parameswaran 134a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 135a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 136a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 137a1cba561SArun Parameswaran if (rc < 0) 138a1cba561SArun Parameswaran return rc; 139a1cba561SArun Parameswaran 140a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 141a1cba561SArun Parameswaran rc = bcm_phy_write_exp(phydev, tmp, val); 142a1cba561SArun Parameswaran 143a1cba561SArun Parameswaran return rc; 144a1cba561SArun Parameswaran } 145a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 146a1cba561SArun Parameswaran 147a1cba561SArun Parameswaran int bcm_phy_read_misc(struct phy_device *phydev, 148a1cba561SArun Parameswaran u16 reg, u16 chl) 149a1cba561SArun Parameswaran { 150a1cba561SArun Parameswaran int rc; 151a1cba561SArun Parameswaran int tmp; 152a1cba561SArun Parameswaran 153a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 154a1cba561SArun Parameswaran MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 155a1cba561SArun Parameswaran if (rc < 0) 156a1cba561SArun Parameswaran return rc; 157a1cba561SArun Parameswaran 158a1cba561SArun Parameswaran tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 159a1cba561SArun Parameswaran tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 160a1cba561SArun Parameswaran rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 161a1cba561SArun Parameswaran if (rc < 0) 162a1cba561SArun Parameswaran return rc; 163a1cba561SArun Parameswaran 164a1cba561SArun Parameswaran tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 165a1cba561SArun Parameswaran rc = bcm_phy_read_exp(phydev, tmp); 166a1cba561SArun Parameswaran 167a1cba561SArun Parameswaran return rc; 168a1cba561SArun Parameswaran } 169a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 170a1cba561SArun Parameswaran 171a1cba561SArun Parameswaran int bcm_phy_ack_intr(struct phy_device *phydev) 172a1cba561SArun Parameswaran { 173a1cba561SArun Parameswaran int reg; 174a1cba561SArun Parameswaran 175a1cba561SArun Parameswaran /* Clear pending interrupts. */ 176a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ISR); 177a1cba561SArun Parameswaran if (reg < 0) 178a1cba561SArun Parameswaran return reg; 179a1cba561SArun Parameswaran 180a1cba561SArun Parameswaran return 0; 181a1cba561SArun Parameswaran } 182a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 183a1cba561SArun Parameswaran 184a1cba561SArun Parameswaran int bcm_phy_config_intr(struct phy_device *phydev) 185a1cba561SArun Parameswaran { 18615772e4dSIoana Ciornei int reg, err; 187a1cba561SArun Parameswaran 188a1cba561SArun Parameswaran reg = phy_read(phydev, MII_BCM54XX_ECR); 189a1cba561SArun Parameswaran if (reg < 0) 190a1cba561SArun Parameswaran return reg; 191a1cba561SArun Parameswaran 19215772e4dSIoana Ciornei if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 19315772e4dSIoana Ciornei err = bcm_phy_ack_intr(phydev); 19415772e4dSIoana Ciornei if (err) 19515772e4dSIoana Ciornei return err; 196a1cba561SArun Parameswaran 19715772e4dSIoana Ciornei reg &= ~MII_BCM54XX_ECR_IM; 19815772e4dSIoana Ciornei err = phy_write(phydev, MII_BCM54XX_ECR, reg); 19915772e4dSIoana Ciornei } else { 20015772e4dSIoana Ciornei reg |= MII_BCM54XX_ECR_IM; 20115772e4dSIoana Ciornei err = phy_write(phydev, MII_BCM54XX_ECR, reg); 20215772e4dSIoana Ciornei if (err) 20315772e4dSIoana Ciornei return err; 20415772e4dSIoana Ciornei 20515772e4dSIoana Ciornei err = bcm_phy_ack_intr(phydev); 20615772e4dSIoana Ciornei } 20715772e4dSIoana Ciornei return err; 208a1cba561SArun Parameswaran } 209a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 210a1cba561SArun Parameswaran 2114567d5c3SIoana Ciornei irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev) 2124567d5c3SIoana Ciornei { 2134567d5c3SIoana Ciornei int irq_status, irq_mask; 2144567d5c3SIoana Ciornei 2154567d5c3SIoana Ciornei irq_status = phy_read(phydev, MII_BCM54XX_ISR); 2164567d5c3SIoana Ciornei if (irq_status < 0) { 2174567d5c3SIoana Ciornei phy_error(phydev); 2184567d5c3SIoana Ciornei return IRQ_NONE; 2194567d5c3SIoana Ciornei } 2204567d5c3SIoana Ciornei 2214567d5c3SIoana Ciornei /* If a bit from the Interrupt Mask register is set, the corresponding 2224567d5c3SIoana Ciornei * bit from the Interrupt Status register is masked. So read the IMR 2234567d5c3SIoana Ciornei * and then flip the bits to get the list of possible interrupt 2244567d5c3SIoana Ciornei * sources. 2254567d5c3SIoana Ciornei */ 2264567d5c3SIoana Ciornei irq_mask = phy_read(phydev, MII_BCM54XX_IMR); 2274567d5c3SIoana Ciornei if (irq_mask < 0) { 2284567d5c3SIoana Ciornei phy_error(phydev); 2294567d5c3SIoana Ciornei return IRQ_NONE; 2304567d5c3SIoana Ciornei } 2314567d5c3SIoana Ciornei irq_mask = ~irq_mask; 2324567d5c3SIoana Ciornei 2334567d5c3SIoana Ciornei if (!(irq_status & irq_mask)) 2344567d5c3SIoana Ciornei return IRQ_NONE; 2354567d5c3SIoana Ciornei 2364567d5c3SIoana Ciornei phy_trigger_machine(phydev); 2374567d5c3SIoana Ciornei 2384567d5c3SIoana Ciornei return IRQ_HANDLED; 2394567d5c3SIoana Ciornei } 2404567d5c3SIoana Ciornei EXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt); 2414567d5c3SIoana Ciornei 242a1cba561SArun Parameswaran int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 243a1cba561SArun Parameswaran { 244a1cba561SArun Parameswaran phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 245a1cba561SArun Parameswaran return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 246a1cba561SArun Parameswaran } 247a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 248a1cba561SArun Parameswaran 249a1cba561SArun Parameswaran int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 250a1cba561SArun Parameswaran u16 val) 251a1cba561SArun Parameswaran { 252a1cba561SArun Parameswaran return phy_write(phydev, MII_BCM54XX_SHD, 253a1cba561SArun Parameswaran MII_BCM54XX_SHD_WRITE | 254a1cba561SArun Parameswaran MII_BCM54XX_SHD_VAL(shadow) | 255a1cba561SArun Parameswaran MII_BCM54XX_SHD_DATA(val)); 256a1cba561SArun Parameswaran } 257a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 258a1cba561SArun Parameswaran 2590a32f1ffSMichael Walle int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 2600a32f1ffSMichael Walle { 2610a32f1ffSMichael Walle int val; 2620a32f1ffSMichael Walle 2630a32f1ffSMichael Walle val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 2640a32f1ffSMichael Walle if (val < 0) 2650a32f1ffSMichael Walle return val; 2660a32f1ffSMichael Walle 2670a32f1ffSMichael Walle return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 2680a32f1ffSMichael Walle } 2690a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 2700a32f1ffSMichael Walle 2710a32f1ffSMichael Walle int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 2720a32f1ffSMichael Walle { 2730a32f1ffSMichael Walle int ret; 2740a32f1ffSMichael Walle 2750a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 2760a32f1ffSMichael Walle ret = __bcm_phy_read_rdb(phydev, rdb); 2770a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 2780a32f1ffSMichael Walle 2790a32f1ffSMichael Walle return ret; 2800a32f1ffSMichael Walle } 2810a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 2820a32f1ffSMichael Walle 2830a32f1ffSMichael Walle int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 2840a32f1ffSMichael Walle { 2850a32f1ffSMichael Walle int ret; 2860a32f1ffSMichael Walle 2870a32f1ffSMichael Walle ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 2880a32f1ffSMichael Walle if (ret < 0) 2890a32f1ffSMichael Walle return ret; 2900a32f1ffSMichael Walle 2910a32f1ffSMichael Walle return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 2920a32f1ffSMichael Walle } 2930a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 2940a32f1ffSMichael Walle 2950a32f1ffSMichael Walle int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 2960a32f1ffSMichael Walle { 2970a32f1ffSMichael Walle int ret; 2980a32f1ffSMichael Walle 2990a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 3000a32f1ffSMichael Walle ret = __bcm_phy_write_rdb(phydev, rdb, val); 3010a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 3020a32f1ffSMichael Walle 3030a32f1ffSMichael Walle return ret; 3040a32f1ffSMichael Walle } 3050a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 3060a32f1ffSMichael Walle 3070a32f1ffSMichael Walle int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 3080a32f1ffSMichael Walle { 3090a32f1ffSMichael Walle int new, ret; 3100a32f1ffSMichael Walle 3110a32f1ffSMichael Walle ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 3120a32f1ffSMichael Walle if (ret < 0) 3130a32f1ffSMichael Walle return ret; 3140a32f1ffSMichael Walle 3150a32f1ffSMichael Walle ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 3160a32f1ffSMichael Walle if (ret < 0) 3170a32f1ffSMichael Walle return ret; 3180a32f1ffSMichael Walle 3190a32f1ffSMichael Walle new = (ret & ~mask) | set; 3200a32f1ffSMichael Walle if (new == ret) 3210a32f1ffSMichael Walle return 0; 3220a32f1ffSMichael Walle 3230a32f1ffSMichael Walle return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 3240a32f1ffSMichael Walle } 3250a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 3260a32f1ffSMichael Walle 3270a32f1ffSMichael Walle int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 3280a32f1ffSMichael Walle { 3290a32f1ffSMichael Walle int ret; 3300a32f1ffSMichael Walle 3310a32f1ffSMichael Walle phy_lock_mdio_bus(phydev); 3320a32f1ffSMichael Walle ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 3330a32f1ffSMichael Walle phy_unlock_mdio_bus(phydev); 3340a32f1ffSMichael Walle 3350a32f1ffSMichael Walle return ret; 3360a32f1ffSMichael Walle } 3370a32f1ffSMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 3380a32f1ffSMichael Walle 339a1cba561SArun Parameswaran int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 340a1cba561SArun Parameswaran { 341a1cba561SArun Parameswaran int val; 342a1cba561SArun Parameswaran 343a1cba561SArun Parameswaran if (dll_pwr_down) { 344a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 345a1cba561SArun Parameswaran if (val < 0) 346a1cba561SArun Parameswaran return val; 347a1cba561SArun Parameswaran 348a1cba561SArun Parameswaran val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 349a1cba561SArun Parameswaran bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 350a1cba561SArun Parameswaran } 351a1cba561SArun Parameswaran 352a1cba561SArun Parameswaran val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 353a1cba561SArun Parameswaran if (val < 0) 354a1cba561SArun Parameswaran return val; 355a1cba561SArun Parameswaran 356a1cba561SArun Parameswaran /* Clear APD bits */ 357a1cba561SArun Parameswaran val &= BCM_APD_CLR_MASK; 358a1cba561SArun Parameswaran 359a1cba561SArun Parameswaran if (phydev->autoneg == AUTONEG_ENABLE) 360a1cba561SArun Parameswaran val |= BCM54XX_SHD_APD_EN; 361a1cba561SArun Parameswaran else 362a1cba561SArun Parameswaran val |= BCM_NO_ANEG_APD_EN; 363a1cba561SArun Parameswaran 364a1cba561SArun Parameswaran /* Enable energy detect single link pulse for easy wakeup */ 365a1cba561SArun Parameswaran val |= BCM_APD_SINGLELP_EN; 366a1cba561SArun Parameswaran 367a1cba561SArun Parameswaran /* Enable Auto Power-Down (APD) for the PHY */ 368a1cba561SArun Parameswaran return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 369a1cba561SArun Parameswaran } 370a1cba561SArun Parameswaran EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 371a1cba561SArun Parameswaran 37299cec8a4SFlorian Fainelli int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 373a1cba561SArun Parameswaran { 374c056d480SFlorian Fainelli int val, mask = 0; 375a1cba561SArun Parameswaran 376a1cba561SArun Parameswaran /* Enable EEE at PHY level */ 377a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 378a1cba561SArun Parameswaran if (val < 0) 379a1cba561SArun Parameswaran return val; 380a1cba561SArun Parameswaran 38199cec8a4SFlorian Fainelli if (enable) 382a1cba561SArun Parameswaran val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 38399cec8a4SFlorian Fainelli else 38499cec8a4SFlorian Fainelli val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 385a1cba561SArun Parameswaran 386a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 387a1cba561SArun Parameswaran 388a1cba561SArun Parameswaran /* Advertise EEE */ 389a6d99fcdSRussell King val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 390a1cba561SArun Parameswaran if (val < 0) 391a1cba561SArun Parameswaran return val; 392a1cba561SArun Parameswaran 393c056d480SFlorian Fainelli if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 394c056d480SFlorian Fainelli phydev->supported)) 395c056d480SFlorian Fainelli mask |= MDIO_EEE_1000T; 396c056d480SFlorian Fainelli if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 397c056d480SFlorian Fainelli phydev->supported)) 398c056d480SFlorian Fainelli mask |= MDIO_EEE_100TX; 399c056d480SFlorian Fainelli 40099cec8a4SFlorian Fainelli if (enable) 401c056d480SFlorian Fainelli val |= mask; 40299cec8a4SFlorian Fainelli else 403c056d480SFlorian Fainelli val &= ~mask; 404a1cba561SArun Parameswaran 405a6d99fcdSRussell King phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 406a1cba561SArun Parameswaran 407a1cba561SArun Parameswaran return 0; 408a1cba561SArun Parameswaran } 40999cec8a4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 410b89eb1fcSArun Parameswaran 411d06f78c4SFlorian Fainelli int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 412d06f78c4SFlorian Fainelli { 413d06f78c4SFlorian Fainelli int val; 414d06f78c4SFlorian Fainelli 415d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 416d06f78c4SFlorian Fainelli if (val < 0) 417d06f78c4SFlorian Fainelli return val; 418d06f78c4SFlorian Fainelli 419d06f78c4SFlorian Fainelli /* Check if wirespeed is enabled or not */ 420d06f78c4SFlorian Fainelli if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 421d06f78c4SFlorian Fainelli *count = DOWNSHIFT_DEV_DISABLE; 422d06f78c4SFlorian Fainelli return 0; 423d06f78c4SFlorian Fainelli } 424d06f78c4SFlorian Fainelli 425d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 426d06f78c4SFlorian Fainelli if (val < 0) 427d06f78c4SFlorian Fainelli return val; 428d06f78c4SFlorian Fainelli 429d06f78c4SFlorian Fainelli /* Downgrade after one link attempt */ 430d06f78c4SFlorian Fainelli if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 431d06f78c4SFlorian Fainelli *count = 1; 432d06f78c4SFlorian Fainelli } else { 433d06f78c4SFlorian Fainelli /* Downgrade after configured retry count */ 434d06f78c4SFlorian Fainelli val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 435d06f78c4SFlorian Fainelli val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 436d06f78c4SFlorian Fainelli *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 437d06f78c4SFlorian Fainelli } 438d06f78c4SFlorian Fainelli 439d06f78c4SFlorian Fainelli return 0; 440d06f78c4SFlorian Fainelli } 441d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 442d06f78c4SFlorian Fainelli 443d06f78c4SFlorian Fainelli int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 444d06f78c4SFlorian Fainelli { 445d06f78c4SFlorian Fainelli int val = 0, ret = 0; 446d06f78c4SFlorian Fainelli 447d06f78c4SFlorian Fainelli /* Range check the number given */ 448d06f78c4SFlorian Fainelli if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 449d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 450d06f78c4SFlorian Fainelli count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 451d06f78c4SFlorian Fainelli return -ERANGE; 452d06f78c4SFlorian Fainelli } 453d06f78c4SFlorian Fainelli 454d06f78c4SFlorian Fainelli val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 455d06f78c4SFlorian Fainelli if (val < 0) 456d06f78c4SFlorian Fainelli return val; 457d06f78c4SFlorian Fainelli 458d06f78c4SFlorian Fainelli /* Se the write enable bit */ 459d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_MISC_WREN; 460d06f78c4SFlorian Fainelli 461d06f78c4SFlorian Fainelli if (count == DOWNSHIFT_DEV_DISABLE) { 462d06f78c4SFlorian Fainelli val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 463d06f78c4SFlorian Fainelli return bcm54xx_auxctl_write(phydev, 464d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 465d06f78c4SFlorian Fainelli val); 466d06f78c4SFlorian Fainelli } else { 467d06f78c4SFlorian Fainelli val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 468d06f78c4SFlorian Fainelli ret = bcm54xx_auxctl_write(phydev, 469d06f78c4SFlorian Fainelli MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 470d06f78c4SFlorian Fainelli val); 471d06f78c4SFlorian Fainelli if (ret < 0) 472d06f78c4SFlorian Fainelli return ret; 473d06f78c4SFlorian Fainelli } 474d06f78c4SFlorian Fainelli 475d06f78c4SFlorian Fainelli val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 476d06f78c4SFlorian Fainelli val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 477d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 478d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 479d06f78c4SFlorian Fainelli 480d06f78c4SFlorian Fainelli switch (count) { 481d06f78c4SFlorian Fainelli case 1: 482d06f78c4SFlorian Fainelli val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 483d06f78c4SFlorian Fainelli break; 484d06f78c4SFlorian Fainelli case DOWNSHIFT_DEV_DEFAULT_COUNT: 485d06f78c4SFlorian Fainelli val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 486d06f78c4SFlorian Fainelli break; 487d06f78c4SFlorian Fainelli default: 488d06f78c4SFlorian Fainelli val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 489d06f78c4SFlorian Fainelli BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 490d06f78c4SFlorian Fainelli break; 491d06f78c4SFlorian Fainelli } 492d06f78c4SFlorian Fainelli 493d06f78c4SFlorian Fainelli return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 494d06f78c4SFlorian Fainelli } 495d06f78c4SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 496d06f78c4SFlorian Fainelli 497820ee17bSFlorian Fainelli struct bcm_phy_hw_stat { 498820ee17bSFlorian Fainelli const char *string; 499820ee17bSFlorian Fainelli u8 reg; 500820ee17bSFlorian Fainelli u8 shift; 501820ee17bSFlorian Fainelli u8 bits; 502820ee17bSFlorian Fainelli }; 503820ee17bSFlorian Fainelli 504820ee17bSFlorian Fainelli /* Counters freeze at either 0xffff or 0xff, better than nothing */ 505820ee17bSFlorian Fainelli static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 506820ee17bSFlorian Fainelli { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 507820ee17bSFlorian Fainelli { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 508820ee17bSFlorian Fainelli { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 509820ee17bSFlorian Fainelli { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 510820ee17bSFlorian Fainelli { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 511820ee17bSFlorian Fainelli }; 512820ee17bSFlorian Fainelli 513820ee17bSFlorian Fainelli int bcm_phy_get_sset_count(struct phy_device *phydev) 514820ee17bSFlorian Fainelli { 515820ee17bSFlorian Fainelli return ARRAY_SIZE(bcm_phy_hw_stats); 516820ee17bSFlorian Fainelli } 517820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 518820ee17bSFlorian Fainelli 519820ee17bSFlorian Fainelli void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 520820ee17bSFlorian Fainelli { 521820ee17bSFlorian Fainelli unsigned int i; 522820ee17bSFlorian Fainelli 523820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 524fb3ceec1SWolfram Sang strscpy(data + i * ETH_GSTRING_LEN, 525820ee17bSFlorian Fainelli bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 526820ee17bSFlorian Fainelli } 527820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 528820ee17bSFlorian Fainelli 529820ee17bSFlorian Fainelli /* Caller is supposed to provide appropriate storage for the library code to 530820ee17bSFlorian Fainelli * access the shadow copy 531820ee17bSFlorian Fainelli */ 532820ee17bSFlorian Fainelli static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 533820ee17bSFlorian Fainelli unsigned int i) 534820ee17bSFlorian Fainelli { 535820ee17bSFlorian Fainelli struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 536820ee17bSFlorian Fainelli int val; 537820ee17bSFlorian Fainelli u64 ret; 538820ee17bSFlorian Fainelli 539820ee17bSFlorian Fainelli val = phy_read(phydev, stat.reg); 540820ee17bSFlorian Fainelli if (val < 0) { 5416c3442f5SJisheng Zhang ret = U64_MAX; 542820ee17bSFlorian Fainelli } else { 543820ee17bSFlorian Fainelli val >>= stat.shift; 544820ee17bSFlorian Fainelli val = val & ((1 << stat.bits) - 1); 545820ee17bSFlorian Fainelli shadow[i] += val; 546820ee17bSFlorian Fainelli ret = shadow[i]; 547820ee17bSFlorian Fainelli } 548820ee17bSFlorian Fainelli 549820ee17bSFlorian Fainelli return ret; 550820ee17bSFlorian Fainelli } 551820ee17bSFlorian Fainelli 552820ee17bSFlorian Fainelli void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 553820ee17bSFlorian Fainelli struct ethtool_stats *stats, u64 *data) 554820ee17bSFlorian Fainelli { 555820ee17bSFlorian Fainelli unsigned int i; 556820ee17bSFlorian Fainelli 557820ee17bSFlorian Fainelli for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 558820ee17bSFlorian Fainelli data[i] = bcm_phy_get_stat(phydev, shadow, i); 559820ee17bSFlorian Fainelli } 560820ee17bSFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 561820ee17bSFlorian Fainelli 562f878fe56SFlorian Fainelli void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 563f878fe56SFlorian Fainelli { 564f878fe56SFlorian Fainelli /* Reset R_CAL/RC_CAL Engine */ 565f878fe56SFlorian Fainelli bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 566f878fe56SFlorian Fainelli 567f878fe56SFlorian Fainelli /* Disable Reset R_AL/RC_CAL Engine */ 568f878fe56SFlorian Fainelli bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 569f878fe56SFlorian Fainelli } 570f878fe56SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 571f878fe56SFlorian Fainelli 572f878fe56SFlorian Fainelli int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 573f878fe56SFlorian Fainelli { 574f878fe56SFlorian Fainelli /* Increase VCO range to prevent unlocking problem of PLL at low 575f878fe56SFlorian Fainelli * temp 576f878fe56SFlorian Fainelli */ 577f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 578f878fe56SFlorian Fainelli 579f878fe56SFlorian Fainelli /* Change Ki to 011 */ 580f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 581f878fe56SFlorian Fainelli 582f878fe56SFlorian Fainelli /* Disable loading of TVCO buffer to bandgap, set bandgap trim 583f878fe56SFlorian Fainelli * to 111 584f878fe56SFlorian Fainelli */ 585f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 586f878fe56SFlorian Fainelli 587f878fe56SFlorian Fainelli /* Adjust bias current trim by -3 */ 588f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 589f878fe56SFlorian Fainelli 590f878fe56SFlorian Fainelli /* Switch to CORE_BASE1E */ 591f878fe56SFlorian Fainelli phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 592f878fe56SFlorian Fainelli 593f878fe56SFlorian Fainelli bcm_phy_r_rc_cal_reset(phydev); 594f878fe56SFlorian Fainelli 595f878fe56SFlorian Fainelli /* write AFE_RXCONFIG_0 */ 596f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 597f878fe56SFlorian Fainelli 598f878fe56SFlorian Fainelli /* write AFE_RXCONFIG_1 */ 599f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 600f878fe56SFlorian Fainelli 601f878fe56SFlorian Fainelli /* write AFE_RX_LP_COUNTER */ 602f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 603f878fe56SFlorian Fainelli 604f878fe56SFlorian Fainelli /* write AFE_HPF_TRIM_OTHERS */ 605f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 606f878fe56SFlorian Fainelli 607f878fe56SFlorian Fainelli /* write AFTE_TX_CONFIG */ 608f878fe56SFlorian Fainelli bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 609f878fe56SFlorian Fainelli 610f878fe56SFlorian Fainelli return 0; 611f878fe56SFlorian Fainelli } 612f878fe56SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 613f878fe56SFlorian Fainelli 614ab41ca34SMurali Krishna Policharla int bcm_phy_enable_jumbo(struct phy_device *phydev) 615ab41ca34SMurali Krishna Policharla { 616ab41ca34SMurali Krishna Policharla int ret; 617ab41ca34SMurali Krishna Policharla 618ab41ca34SMurali Krishna Policharla ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 619ab41ca34SMurali Krishna Policharla if (ret < 0) 620ab41ca34SMurali Krishna Policharla return ret; 621ab41ca34SMurali Krishna Policharla 622ab41ca34SMurali Krishna Policharla /* Enable extended length packet reception */ 623ab41ca34SMurali Krishna Policharla ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 624ab41ca34SMurali Krishna Policharla ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 625ab41ca34SMurali Krishna Policharla if (ret < 0) 626ab41ca34SMurali Krishna Policharla return ret; 627ab41ca34SMurali Krishna Policharla 628ab41ca34SMurali Krishna Policharla /* Enable the elastic FIFO for raising the transmission limit from 629ab41ca34SMurali Krishna Policharla * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 630ab41ca34SMurali Krishna Policharla * latency. 631ab41ca34SMurali Krishna Policharla */ 632ab41ca34SMurali Krishna Policharla return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 633ab41ca34SMurali Krishna Policharla } 634ab41ca34SMurali Krishna Policharla EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 635ab41ca34SMurali Krishna Policharla 6363190ca3bSMichael Walle static int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 63711ecf8c5SMichael Walle { 63811ecf8c5SMichael Walle return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 63911ecf8c5SMichael Walle } 64011ecf8c5SMichael Walle 6413190ca3bSMichael Walle static int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 64211ecf8c5SMichael Walle { 64311ecf8c5SMichael Walle return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 64411ecf8c5SMichael Walle BCM54XX_ACCESS_MODE_LEGACY_EN); 64511ecf8c5SMichael Walle } 64611ecf8c5SMichael Walle 64711ecf8c5SMichael Walle static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 64811ecf8c5SMichael Walle { 64911ecf8c5SMichael Walle u16 mask, set; 65011ecf8c5SMichael Walle int ret; 65111ecf8c5SMichael Walle 65211ecf8c5SMichael Walle /* Auto-negotiation must be enabled for cable diagnostics to work, but 65311ecf8c5SMichael Walle * don't advertise any capabilities. 65411ecf8c5SMichael Walle */ 65511ecf8c5SMichael Walle phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 65611ecf8c5SMichael Walle phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 65711ecf8c5SMichael Walle phy_write(phydev, MII_CTRL1000, 0); 65811ecf8c5SMichael Walle 65911ecf8c5SMichael Walle phy_lock_mdio_bus(phydev); 66011ecf8c5SMichael Walle if (is_rdb) { 66111ecf8c5SMichael Walle ret = __bcm_phy_enable_legacy_access(phydev); 66211ecf8c5SMichael Walle if (ret) 66311ecf8c5SMichael Walle goto out; 66411ecf8c5SMichael Walle } 66511ecf8c5SMichael Walle 66611ecf8c5SMichael Walle mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 66711ecf8c5SMichael Walle set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 66811ecf8c5SMichael Walle FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 66911ecf8c5SMichael Walle BCM54XX_ECD_CTRL_UNIT_CM); 67011ecf8c5SMichael Walle 67111ecf8c5SMichael Walle ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 67211ecf8c5SMichael Walle 67311ecf8c5SMichael Walle out: 67411ecf8c5SMichael Walle /* re-enable the RDB access even if there was an error */ 67511ecf8c5SMichael Walle if (is_rdb) 67611ecf8c5SMichael Walle ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 67711ecf8c5SMichael Walle 67811ecf8c5SMichael Walle phy_unlock_mdio_bus(phydev); 67911ecf8c5SMichael Walle 68011ecf8c5SMichael Walle return ret; 68111ecf8c5SMichael Walle } 68211ecf8c5SMichael Walle 68311ecf8c5SMichael Walle static int bcm_phy_cable_test_report_trans(int result) 68411ecf8c5SMichael Walle { 68511ecf8c5SMichael Walle switch (result) { 68611ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OK: 68711ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_OK; 68811ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OPEN: 68911ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 69011ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 69111ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 69211ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 69311ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 69411ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_INVALID: 69511ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_BUSY: 69611ecf8c5SMichael Walle default: 69711ecf8c5SMichael Walle return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 69811ecf8c5SMichael Walle } 69911ecf8c5SMichael Walle } 70011ecf8c5SMichael Walle 70111ecf8c5SMichael Walle static bool bcm_phy_distance_valid(int result) 70211ecf8c5SMichael Walle { 70311ecf8c5SMichael Walle switch (result) { 70411ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_OPEN: 70511ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 70611ecf8c5SMichael Walle case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 70711ecf8c5SMichael Walle return true; 70811ecf8c5SMichael Walle } 70911ecf8c5SMichael Walle return false; 71011ecf8c5SMichael Walle } 71111ecf8c5SMichael Walle 71211ecf8c5SMichael Walle static int bcm_phy_report_length(struct phy_device *phydev, int pair) 71311ecf8c5SMichael Walle { 71411ecf8c5SMichael Walle int val; 71511ecf8c5SMichael Walle 71611ecf8c5SMichael Walle val = __bcm_phy_read_exp(phydev, 71711ecf8c5SMichael Walle BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 71811ecf8c5SMichael Walle if (val < 0) 71911ecf8c5SMichael Walle return val; 72011ecf8c5SMichael Walle 72111ecf8c5SMichael Walle if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 72211ecf8c5SMichael Walle return 0; 72311ecf8c5SMichael Walle 72411ecf8c5SMichael Walle ethnl_cable_test_fault_length(phydev, pair, val); 72511ecf8c5SMichael Walle 72611ecf8c5SMichael Walle return 0; 72711ecf8c5SMichael Walle } 72811ecf8c5SMichael Walle 72911ecf8c5SMichael Walle static int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 73011ecf8c5SMichael Walle bool *finished, bool is_rdb) 73111ecf8c5SMichael Walle { 73211ecf8c5SMichael Walle int pair_a, pair_b, pair_c, pair_d, ret; 73311ecf8c5SMichael Walle 73411ecf8c5SMichael Walle *finished = false; 73511ecf8c5SMichael Walle 73611ecf8c5SMichael Walle phy_lock_mdio_bus(phydev); 73711ecf8c5SMichael Walle 73811ecf8c5SMichael Walle if (is_rdb) { 73911ecf8c5SMichael Walle ret = __bcm_phy_enable_legacy_access(phydev); 74011ecf8c5SMichael Walle if (ret) 74111ecf8c5SMichael Walle goto out; 74211ecf8c5SMichael Walle } 74311ecf8c5SMichael Walle 74411ecf8c5SMichael Walle ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 74511ecf8c5SMichael Walle if (ret < 0) 74611ecf8c5SMichael Walle goto out; 74711ecf8c5SMichael Walle 74811ecf8c5SMichael Walle if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 74911ecf8c5SMichael Walle ret = 0; 75011ecf8c5SMichael Walle goto out; 75111ecf8c5SMichael Walle } 75211ecf8c5SMichael Walle 75311ecf8c5SMichael Walle ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 75411ecf8c5SMichael Walle if (ret < 0) 75511ecf8c5SMichael Walle goto out; 75611ecf8c5SMichael Walle 75711ecf8c5SMichael Walle pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 75811ecf8c5SMichael Walle pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 75911ecf8c5SMichael Walle pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 76011ecf8c5SMichael Walle pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 76111ecf8c5SMichael Walle 76211ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 76311ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_a)); 76411ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 76511ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_b)); 76611ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 76711ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_c)); 76811ecf8c5SMichael Walle ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 76911ecf8c5SMichael Walle bcm_phy_cable_test_report_trans(pair_d)); 77011ecf8c5SMichael Walle 77111ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_a)) 77211ecf8c5SMichael Walle bcm_phy_report_length(phydev, 0); 77311ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_b)) 77411ecf8c5SMichael Walle bcm_phy_report_length(phydev, 1); 77511ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_c)) 77611ecf8c5SMichael Walle bcm_phy_report_length(phydev, 2); 77711ecf8c5SMichael Walle if (bcm_phy_distance_valid(pair_d)) 77811ecf8c5SMichael Walle bcm_phy_report_length(phydev, 3); 77911ecf8c5SMichael Walle 78011ecf8c5SMichael Walle ret = 0; 78111ecf8c5SMichael Walle *finished = true; 78211ecf8c5SMichael Walle out: 78311ecf8c5SMichael Walle /* re-enable the RDB access even if there was an error */ 78411ecf8c5SMichael Walle if (is_rdb) 78511ecf8c5SMichael Walle ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 78611ecf8c5SMichael Walle 78711ecf8c5SMichael Walle phy_unlock_mdio_bus(phydev); 78811ecf8c5SMichael Walle 78911ecf8c5SMichael Walle return ret; 79011ecf8c5SMichael Walle } 79111ecf8c5SMichael Walle 79211ecf8c5SMichael Walle int bcm_phy_cable_test_start(struct phy_device *phydev) 79311ecf8c5SMichael Walle { 79411ecf8c5SMichael Walle return _bcm_phy_cable_test_start(phydev, false); 79511ecf8c5SMichael Walle } 79611ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 79711ecf8c5SMichael Walle 79811ecf8c5SMichael Walle int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 79911ecf8c5SMichael Walle { 80011ecf8c5SMichael Walle return _bcm_phy_cable_test_get_status(phydev, finished, false); 80111ecf8c5SMichael Walle } 80211ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 80311ecf8c5SMichael Walle 80411ecf8c5SMichael Walle /* We assume that all PHYs which support RDB access can be switched to legacy 80511ecf8c5SMichael Walle * mode. If, in the future, this is not true anymore, we have to re-implement 80611ecf8c5SMichael Walle * this with RDB access. 80711ecf8c5SMichael Walle */ 80811ecf8c5SMichael Walle int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 80911ecf8c5SMichael Walle { 81011ecf8c5SMichael Walle return _bcm_phy_cable_test_start(phydev, true); 81111ecf8c5SMichael Walle } 81211ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 81311ecf8c5SMichael Walle 81411ecf8c5SMichael Walle int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 81511ecf8c5SMichael Walle bool *finished) 81611ecf8c5SMichael Walle { 81711ecf8c5SMichael Walle return _bcm_phy_cable_test_get_status(phydev, finished, true); 81811ecf8c5SMichael Walle } 81911ecf8c5SMichael Walle EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 82011ecf8c5SMichael Walle 8218baddaa9SFlorian Fainelli #define BCM54XX_WOL_SUPPORTED_MASK (WAKE_UCAST | \ 8228baddaa9SFlorian Fainelli WAKE_MCAST | \ 8238baddaa9SFlorian Fainelli WAKE_BCAST | \ 8248baddaa9SFlorian Fainelli WAKE_MAGIC | \ 8258baddaa9SFlorian Fainelli WAKE_MAGICSECURE) 8268baddaa9SFlorian Fainelli 8278baddaa9SFlorian Fainelli int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 8288baddaa9SFlorian Fainelli { 8298baddaa9SFlorian Fainelli struct net_device *ndev = phydev->attached_dev; 8308baddaa9SFlorian Fainelli u8 da[ETH_ALEN], mask[ETH_ALEN]; 8318baddaa9SFlorian Fainelli unsigned int i; 8328baddaa9SFlorian Fainelli u16 ctl; 8338baddaa9SFlorian Fainelli int ret; 8348baddaa9SFlorian Fainelli 8358baddaa9SFlorian Fainelli /* Allow a MAC driver to play through its own Wake-on-LAN 8368baddaa9SFlorian Fainelli * implementation 8378baddaa9SFlorian Fainelli */ 8388baddaa9SFlorian Fainelli if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK) 8398baddaa9SFlorian Fainelli return -EOPNOTSUPP; 8408baddaa9SFlorian Fainelli 8418baddaa9SFlorian Fainelli /* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's 8428baddaa9SFlorian Fainelli * ethtool only supports 6, for now. 8438baddaa9SFlorian Fainelli */ 8448baddaa9SFlorian Fainelli BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN); 8458baddaa9SFlorian Fainelli 8468baddaa9SFlorian Fainelli /* Clear previous interrupts */ 8478baddaa9SFlorian Fainelli ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); 8488baddaa9SFlorian Fainelli if (ret < 0) 8498baddaa9SFlorian Fainelli return ret; 8508baddaa9SFlorian Fainelli 8518baddaa9SFlorian Fainelli ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); 8528baddaa9SFlorian Fainelli if (ret < 0) 8538baddaa9SFlorian Fainelli return ret; 8548baddaa9SFlorian Fainelli 8558baddaa9SFlorian Fainelli ctl = ret; 8568baddaa9SFlorian Fainelli 8578baddaa9SFlorian Fainelli if (!wol->wolopts) { 8588baddaa9SFlorian Fainelli if (phy_interrupt_is_valid(phydev)) 8598baddaa9SFlorian Fainelli disable_irq_wake(phydev->irq); 8608baddaa9SFlorian Fainelli 8618baddaa9SFlorian Fainelli /* Leave all interrupts disabled */ 8628baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 8638baddaa9SFlorian Fainelli BCM54XX_WOL_ALL_INTRS); 8648baddaa9SFlorian Fainelli if (ret < 0) 8658baddaa9SFlorian Fainelli return ret; 8668baddaa9SFlorian Fainelli 8678baddaa9SFlorian Fainelli /* Disable the global Wake-on-LAN enable bit */ 8688baddaa9SFlorian Fainelli ctl &= ~BCM54XX_WOL_EN; 8698baddaa9SFlorian Fainelli 8708baddaa9SFlorian Fainelli return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); 8718baddaa9SFlorian Fainelli } 8728baddaa9SFlorian Fainelli 8738baddaa9SFlorian Fainelli /* Clear the previously configured mode and mask mode for Wake-on-LAN */ 8748baddaa9SFlorian Fainelli ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT); 8758baddaa9SFlorian Fainelli ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT); 8768baddaa9SFlorian Fainelli ctl &= ~BCM54XX_WOL_DIR_PKT_EN; 8778baddaa9SFlorian Fainelli ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT); 8788baddaa9SFlorian Fainelli 8798baddaa9SFlorian Fainelli /* When using WAKE_MAGIC, we program the magic pattern filter to match 8808baddaa9SFlorian Fainelli * the device's MAC address and we accept any MAC DA in the Ethernet 8818baddaa9SFlorian Fainelli * frame. 8828baddaa9SFlorian Fainelli * 8838baddaa9SFlorian Fainelli * When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the 8848baddaa9SFlorian Fainelli * following: 8858baddaa9SFlorian Fainelli * - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match 8868baddaa9SFlorian Fainelli * - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care 8878baddaa9SFlorian Fainelli * - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match 8888baddaa9SFlorian Fainelli * 8898baddaa9SFlorian Fainelli * Note that the Broadcast MAC DA is inherently going to match the 8908baddaa9SFlorian Fainelli * multicast pattern being matched. 8918baddaa9SFlorian Fainelli */ 8928baddaa9SFlorian Fainelli memset(mask, 0, sizeof(mask)); 8938baddaa9SFlorian Fainelli 8948baddaa9SFlorian Fainelli if (wol->wolopts & WAKE_MCAST) { 8958baddaa9SFlorian Fainelli memset(da, 0, sizeof(da)); 8968baddaa9SFlorian Fainelli memset(mask, 0xff, sizeof(mask)); 8978baddaa9SFlorian Fainelli da[0] = 0x01; 8988baddaa9SFlorian Fainelli mask[0] = ~da[0]; 8998baddaa9SFlorian Fainelli } else { 9008baddaa9SFlorian Fainelli if (wol->wolopts & WAKE_UCAST) { 9018baddaa9SFlorian Fainelli ether_addr_copy(da, ndev->dev_addr); 9028baddaa9SFlorian Fainelli } else if (wol->wolopts & WAKE_BCAST) { 9038baddaa9SFlorian Fainelli eth_broadcast_addr(da); 9048baddaa9SFlorian Fainelli } else if (wol->wolopts & WAKE_MAGICSECURE) { 9058baddaa9SFlorian Fainelli ether_addr_copy(da, wol->sopass); 9068baddaa9SFlorian Fainelli } else if (wol->wolopts & WAKE_MAGIC) { 9078baddaa9SFlorian Fainelli memset(da, 0, sizeof(da)); 9088baddaa9SFlorian Fainelli memset(mask, 0xff, sizeof(mask)); 9098baddaa9SFlorian Fainelli } 9108baddaa9SFlorian Fainelli } 9118baddaa9SFlorian Fainelli 9128baddaa9SFlorian Fainelli for (i = 0; i < ETH_ALEN / 2; i++) { 9138baddaa9SFlorian Fainelli if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { 9148baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, 9158baddaa9SFlorian Fainelli BCM54XX_WOL_MPD_DATA1(2 - i), 9168baddaa9SFlorian Fainelli ndev->dev_addr[i * 2] << 8 | 9178baddaa9SFlorian Fainelli ndev->dev_addr[i * 2 + 1]); 9188baddaa9SFlorian Fainelli if (ret < 0) 9198baddaa9SFlorian Fainelli return ret; 9208baddaa9SFlorian Fainelli } 9218baddaa9SFlorian Fainelli 9228baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i), 9238baddaa9SFlorian Fainelli da[i * 2] << 8 | da[i * 2 + 1]); 9248baddaa9SFlorian Fainelli if (ret < 0) 9258baddaa9SFlorian Fainelli return ret; 9268baddaa9SFlorian Fainelli 9278baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i), 9288baddaa9SFlorian Fainelli mask[i * 2] << 8 | mask[i * 2 + 1]); 9298baddaa9SFlorian Fainelli if (ret) 9308baddaa9SFlorian Fainelli return ret; 9318baddaa9SFlorian Fainelli } 9328baddaa9SFlorian Fainelli 9338baddaa9SFlorian Fainelli if (wol->wolopts & WAKE_MAGICSECURE) { 9348baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_SECKEY_OPT_6B << 9358baddaa9SFlorian Fainelli BCM54XX_WOL_SECKEY_OPT_SHIFT; 9368baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT; 9378baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_MASK_MODE_DA_FF << 9388baddaa9SFlorian Fainelli BCM54XX_WOL_MASK_MODE_SHIFT; 9398baddaa9SFlorian Fainelli } else { 9408baddaa9SFlorian Fainelli if (wol->wolopts & WAKE_MAGIC) 9418baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_MODE_SINGLE_MPD; 9428baddaa9SFlorian Fainelli else 9438baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_DIR_PKT_EN; 9448baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY << 9458baddaa9SFlorian Fainelli BCM54XX_WOL_MASK_MODE_SHIFT; 9468baddaa9SFlorian Fainelli } 9478baddaa9SFlorian Fainelli 9488baddaa9SFlorian Fainelli /* Globally enable Wake-on-LAN */ 9498baddaa9SFlorian Fainelli ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK; 9508baddaa9SFlorian Fainelli 9518baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); 9528baddaa9SFlorian Fainelli if (ret < 0) 9538baddaa9SFlorian Fainelli return ret; 9548baddaa9SFlorian Fainelli 9558baddaa9SFlorian Fainelli /* Enable WOL interrupt on LED4 */ 9568baddaa9SFlorian Fainelli ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL); 9578baddaa9SFlorian Fainelli if (ret < 0) 9588baddaa9SFlorian Fainelli return ret; 9598baddaa9SFlorian Fainelli 9608baddaa9SFlorian Fainelli ret |= BCM54XX_LED4_SEL_INTR; 9618baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret); 9628baddaa9SFlorian Fainelli if (ret < 0) 9638baddaa9SFlorian Fainelli return ret; 9648baddaa9SFlorian Fainelli 9658baddaa9SFlorian Fainelli /* Enable all Wake-on-LAN interrupt sources */ 9668baddaa9SFlorian Fainelli ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0); 9678baddaa9SFlorian Fainelli if (ret < 0) 9688baddaa9SFlorian Fainelli return ret; 9698baddaa9SFlorian Fainelli 9708baddaa9SFlorian Fainelli if (phy_interrupt_is_valid(phydev)) 9718baddaa9SFlorian Fainelli enable_irq_wake(phydev->irq); 9728baddaa9SFlorian Fainelli 9738baddaa9SFlorian Fainelli return 0; 9748baddaa9SFlorian Fainelli } 9758baddaa9SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_set_wol); 9768baddaa9SFlorian Fainelli 9778baddaa9SFlorian Fainelli void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 9788baddaa9SFlorian Fainelli { 9798baddaa9SFlorian Fainelli struct net_device *ndev = phydev->attached_dev; 9808baddaa9SFlorian Fainelli u8 da[ETH_ALEN]; 9818baddaa9SFlorian Fainelli unsigned int i; 9828baddaa9SFlorian Fainelli int ret; 9838baddaa9SFlorian Fainelli u16 ctl; 9848baddaa9SFlorian Fainelli 9858baddaa9SFlorian Fainelli wol->supported = BCM54XX_WOL_SUPPORTED_MASK; 9868baddaa9SFlorian Fainelli wol->wolopts = 0; 9878baddaa9SFlorian Fainelli 9888baddaa9SFlorian Fainelli ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); 9898baddaa9SFlorian Fainelli if (ret < 0) 9908baddaa9SFlorian Fainelli return; 9918baddaa9SFlorian Fainelli 9928baddaa9SFlorian Fainelli ctl = ret; 9938baddaa9SFlorian Fainelli 9948baddaa9SFlorian Fainelli if (!(ctl & BCM54XX_WOL_EN)) 9958baddaa9SFlorian Fainelli return; 9968baddaa9SFlorian Fainelli 9978baddaa9SFlorian Fainelli for (i = 0; i < sizeof(da) / 2; i++) { 9988baddaa9SFlorian Fainelli ret = bcm_phy_read_exp(phydev, 9998baddaa9SFlorian Fainelli BCM54XX_WOL_MPD_DATA2(2 - i)); 10008baddaa9SFlorian Fainelli if (ret < 0) 10018baddaa9SFlorian Fainelli return; 10028baddaa9SFlorian Fainelli 10038baddaa9SFlorian Fainelli da[i * 2] = ret >> 8; 10048baddaa9SFlorian Fainelli da[i * 2 + 1] = ret & 0xff; 10058baddaa9SFlorian Fainelli } 10068baddaa9SFlorian Fainelli 10078baddaa9SFlorian Fainelli if (ctl & BCM54XX_WOL_DIR_PKT_EN) { 10088baddaa9SFlorian Fainelli if (is_broadcast_ether_addr(da)) 10098baddaa9SFlorian Fainelli wol->wolopts |= WAKE_BCAST; 10108baddaa9SFlorian Fainelli else if (is_multicast_ether_addr(da)) 10118baddaa9SFlorian Fainelli wol->wolopts |= WAKE_MCAST; 10128baddaa9SFlorian Fainelli else if (ether_addr_equal(da, ndev->dev_addr)) 10138baddaa9SFlorian Fainelli wol->wolopts |= WAKE_UCAST; 10148baddaa9SFlorian Fainelli } else { 10158baddaa9SFlorian Fainelli ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK; 10168baddaa9SFlorian Fainelli switch (ctl) { 10178baddaa9SFlorian Fainelli case BCM54XX_WOL_MODE_SINGLE_MPD: 10188baddaa9SFlorian Fainelli wol->wolopts |= WAKE_MAGIC; 10198baddaa9SFlorian Fainelli break; 10208baddaa9SFlorian Fainelli case BCM54XX_WOL_MODE_SINGLE_MPDSEC: 10218baddaa9SFlorian Fainelli wol->wolopts |= WAKE_MAGICSECURE; 10228baddaa9SFlorian Fainelli memcpy(wol->sopass, da, sizeof(da)); 10238baddaa9SFlorian Fainelli break; 10248baddaa9SFlorian Fainelli default: 10258baddaa9SFlorian Fainelli break; 10268baddaa9SFlorian Fainelli } 10278baddaa9SFlorian Fainelli } 10288baddaa9SFlorian Fainelli } 10298baddaa9SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_get_wol); 10308baddaa9SFlorian Fainelli 1031*4781e965SFlorian Fainelli irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id) 1032*4781e965SFlorian Fainelli { 1033*4781e965SFlorian Fainelli return IRQ_HANDLED; 1034*4781e965SFlorian Fainelli } 1035*4781e965SFlorian Fainelli EXPORT_SYMBOL_GPL(bcm_phy_wol_isr); 1036*4781e965SFlorian Fainelli 1037b89eb1fcSArun Parameswaran MODULE_DESCRIPTION("Broadcom PHY Library"); 1038b89eb1fcSArun Parameswaran MODULE_LICENSE("GPL v2"); 1039b89eb1fcSArun Parameswaran MODULE_AUTHOR("Broadcom Corporation"); 1040