162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015-2017 Broadcom 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "bcm-phy-lib.h" 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/brcmphy.h> 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/mdio.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/phy.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/ethtool_netlink.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MII_BCM_CHANNEL_WIDTH 0x2000 1962306a36Sopenharmony_ci#define BCM_CL45VEN_EEE_ADV 0x3c 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciint __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int rc; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 2662306a36Sopenharmony_ci if (rc < 0) 2762306a36Sopenharmony_ci return rc; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_write_exp); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciint bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int rc; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 3862306a36Sopenharmony_ci rc = __bcm_phy_write_exp(phydev, reg, val); 3962306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return rc; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_exp); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciint __bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int val; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 5062306a36Sopenharmony_ci if (val < 0) 5162306a36Sopenharmony_ci return val; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci val = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Restore default value. It's O.K. if this write fails. */ 5662306a36Sopenharmony_ci __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return val; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_read_exp); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int rc; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 6762306a36Sopenharmony_ci rc = __bcm_phy_read_exp(phydev, reg); 6862306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return rc; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_exp); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciint __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int new, ret; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 7962306a36Sopenharmony_ci if (ret < 0) 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 8362306a36Sopenharmony_ci if (ret < 0) 8462306a36Sopenharmony_ci return ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci new = (ret & ~mask) | set; 8762306a36Sopenharmony_ci if (new == ret) 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_modify_exp); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciint bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int ret; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 9962306a36Sopenharmony_ci ret = __bcm_phy_modify_exp(phydev, reg, mask, set); 10062306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return ret; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_modify_exp); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciint bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci /* The register must be written to both the Shadow Register Select and 10962306a36Sopenharmony_ci * the Shadow Read Register Selector 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK | 11262306a36Sopenharmony_ci regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); 11362306a36Sopenharmony_ci return phy_read(phydev, MII_BCM54XX_AUX_CTL); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciEXPORT_SYMBOL(bcm54xx_auxctl_write); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint bcm_phy_write_misc(struct phy_device *phydev, 12462306a36Sopenharmony_ci u16 reg, u16 chl, u16 val) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int rc; 12762306a36Sopenharmony_ci int tmp; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 13062306a36Sopenharmony_ci MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 13162306a36Sopenharmony_ci if (rc < 0) 13262306a36Sopenharmony_ci return rc; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 13562306a36Sopenharmony_ci tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 13662306a36Sopenharmony_ci rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 13762306a36Sopenharmony_ci if (rc < 0) 13862306a36Sopenharmony_ci return rc; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 14162306a36Sopenharmony_ci rc = bcm_phy_write_exp(phydev, tmp, val); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return rc; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_misc); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciint bcm_phy_read_misc(struct phy_device *phydev, 14862306a36Sopenharmony_ci u16 reg, u16 chl) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int rc; 15162306a36Sopenharmony_ci int tmp; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 15462306a36Sopenharmony_ci MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 15562306a36Sopenharmony_ci if (rc < 0) 15662306a36Sopenharmony_ci return rc; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 15962306a36Sopenharmony_ci tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 16062306a36Sopenharmony_ci rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 16162306a36Sopenharmony_ci if (rc < 0) 16262306a36Sopenharmony_ci return rc; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 16562306a36Sopenharmony_ci rc = bcm_phy_read_exp(phydev, tmp); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return rc; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_misc); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint bcm_phy_ack_intr(struct phy_device *phydev) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int reg; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Clear pending interrupts. */ 17662306a36Sopenharmony_ci reg = phy_read(phydev, MII_BCM54XX_ISR); 17762306a36Sopenharmony_ci if (reg < 0) 17862306a36Sopenharmony_ci return reg; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint bcm_phy_config_intr(struct phy_device *phydev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int reg, err; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci reg = phy_read(phydev, MII_BCM54XX_ECR); 18962306a36Sopenharmony_ci if (reg < 0) 19062306a36Sopenharmony_ci return reg; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 19362306a36Sopenharmony_ci err = bcm_phy_ack_intr(phydev); 19462306a36Sopenharmony_ci if (err) 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci reg &= ~MII_BCM54XX_ECR_IM; 19862306a36Sopenharmony_ci err = phy_write(phydev, MII_BCM54XX_ECR, reg); 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci reg |= MII_BCM54XX_ECR_IM; 20162306a36Sopenharmony_ci err = phy_write(phydev, MII_BCM54XX_ECR, reg); 20262306a36Sopenharmony_ci if (err) 20362306a36Sopenharmony_ci return err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci err = bcm_phy_ack_intr(phydev); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci return err; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_config_intr); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ciirqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int irq_status, irq_mask; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_BCM54XX_ISR); 21662306a36Sopenharmony_ci if (irq_status < 0) { 21762306a36Sopenharmony_ci phy_error(phydev); 21862306a36Sopenharmony_ci return IRQ_NONE; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* If a bit from the Interrupt Mask register is set, the corresponding 22262306a36Sopenharmony_ci * bit from the Interrupt Status register is masked. So read the IMR 22362306a36Sopenharmony_ci * and then flip the bits to get the list of possible interrupt 22462306a36Sopenharmony_ci * sources. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci irq_mask = phy_read(phydev, MII_BCM54XX_IMR); 22762306a36Sopenharmony_ci if (irq_mask < 0) { 22862306a36Sopenharmony_ci phy_error(phydev); 22962306a36Sopenharmony_ci return IRQ_NONE; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci irq_mask = ~irq_mask; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (!(irq_status & irq_mask)) 23462306a36Sopenharmony_ci return IRQ_NONE; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci phy_trigger_machine(phydev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return IRQ_HANDLED; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciint bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 24562306a36Sopenharmony_ci return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 25062306a36Sopenharmony_ci u16 val) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci return phy_write(phydev, MII_BCM54XX_SHD, 25362306a36Sopenharmony_ci MII_BCM54XX_SHD_WRITE | 25462306a36Sopenharmony_ci MII_BCM54XX_SHD_VAL(shadow) | 25562306a36Sopenharmony_ci MII_BCM54XX_SHD_DATA(val)); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int val; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 26462306a36Sopenharmony_ci if (val < 0) 26562306a36Sopenharmony_ci return val; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 27662306a36Sopenharmony_ci ret = __bcm_phy_read_rdb(phydev, rdb); 27762306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci int ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 28862306a36Sopenharmony_ci if (ret < 0) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciint bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 30062306a36Sopenharmony_ci ret = __bcm_phy_write_rdb(phydev, rdb, val); 30162306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciint __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int new, ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 31662306a36Sopenharmony_ci if (ret < 0) 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci new = (ret & ~mask) | set; 32062306a36Sopenharmony_ci if (new == ret) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciint bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 33262306a36Sopenharmony_ci ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 33362306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ciint bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int val; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (dll_pwr_down) { 34462306a36Sopenharmony_ci val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 34562306a36Sopenharmony_ci if (val < 0) 34662306a36Sopenharmony_ci return val; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 34962306a36Sopenharmony_ci bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 35362306a36Sopenharmony_ci if (val < 0) 35462306a36Sopenharmony_ci return val; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Clear APD bits */ 35762306a36Sopenharmony_ci val &= BCM_APD_CLR_MASK; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (phydev->autoneg == AUTONEG_ENABLE) 36062306a36Sopenharmony_ci val |= BCM54XX_SHD_APD_EN; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci val |= BCM_NO_ANEG_APD_EN; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Enable energy detect single link pulse for easy wakeup */ 36562306a36Sopenharmony_ci val |= BCM_APD_SINGLELP_EN; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Enable Auto Power-Down (APD) for the PHY */ 36862306a36Sopenharmony_ci return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciint bcm_phy_set_eee(struct phy_device *phydev, bool enable) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int val, mask = 0; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Enable EEE at PHY level */ 37762306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 37862306a36Sopenharmony_ci if (val < 0) 37962306a36Sopenharmony_ci return val; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (enable) 38262306a36Sopenharmony_ci val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 38362306a36Sopenharmony_ci else 38462306a36Sopenharmony_ci val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Advertise EEE */ 38962306a36Sopenharmony_ci val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 39062306a36Sopenharmony_ci if (val < 0) 39162306a36Sopenharmony_ci return val; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 39462306a36Sopenharmony_ci phydev->supported)) 39562306a36Sopenharmony_ci mask |= MDIO_EEE_1000T; 39662306a36Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 39762306a36Sopenharmony_ci phydev->supported)) 39862306a36Sopenharmony_ci mask |= MDIO_EEE_100TX; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (enable) 40162306a36Sopenharmony_ci val |= mask; 40262306a36Sopenharmony_ci else 40362306a36Sopenharmony_ci val &= ~mask; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_set_eee); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ciint bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci int val; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 41662306a36Sopenharmony_ci if (val < 0) 41762306a36Sopenharmony_ci return val; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Check if wirespeed is enabled or not */ 42062306a36Sopenharmony_ci if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 42162306a36Sopenharmony_ci *count = DOWNSHIFT_DEV_DISABLE; 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 42662306a36Sopenharmony_ci if (val < 0) 42762306a36Sopenharmony_ci return val; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Downgrade after one link attempt */ 43062306a36Sopenharmony_ci if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 43162306a36Sopenharmony_ci *count = 1; 43262306a36Sopenharmony_ci } else { 43362306a36Sopenharmony_ci /* Downgrade after configured retry count */ 43462306a36Sopenharmony_ci val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 43562306a36Sopenharmony_ci val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 43662306a36Sopenharmony_ci *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciint bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int val = 0, ret = 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Range check the number given */ 44862306a36Sopenharmony_ci if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 44962306a36Sopenharmony_ci BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 45062306a36Sopenharmony_ci count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 45162306a36Sopenharmony_ci return -ERANGE; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 45562306a36Sopenharmony_ci if (val < 0) 45662306a36Sopenharmony_ci return val; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Se the write enable bit */ 45962306a36Sopenharmony_ci val |= MII_BCM54XX_AUXCTL_MISC_WREN; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (count == DOWNSHIFT_DEV_DISABLE) { 46262306a36Sopenharmony_ci val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 46362306a36Sopenharmony_ci return bcm54xx_auxctl_write(phydev, 46462306a36Sopenharmony_ci MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 46562306a36Sopenharmony_ci val); 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 46862306a36Sopenharmony_ci ret = bcm54xx_auxctl_write(phydev, 46962306a36Sopenharmony_ci MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 47062306a36Sopenharmony_ci val); 47162306a36Sopenharmony_ci if (ret < 0) 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 47662306a36Sopenharmony_ci val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 47762306a36Sopenharmony_ci BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 47862306a36Sopenharmony_ci BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci switch (count) { 48162306a36Sopenharmony_ci case 1: 48262306a36Sopenharmony_ci val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case DOWNSHIFT_DEV_DEFAULT_COUNT: 48562306a36Sopenharmony_ci val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci default: 48862306a36Sopenharmony_ci val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 48962306a36Sopenharmony_ci BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistruct bcm_phy_hw_stat { 49862306a36Sopenharmony_ci const char *string; 49962306a36Sopenharmony_ci int devad; 50062306a36Sopenharmony_ci u16 reg; 50162306a36Sopenharmony_ci u8 shift; 50262306a36Sopenharmony_ci u8 bits; 50362306a36Sopenharmony_ci}; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/* Counters freeze at either 0xffff or 0xff, better than nothing */ 50662306a36Sopenharmony_cistatic const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 50762306a36Sopenharmony_ci { "phy_receive_errors", -1, MII_BRCM_CORE_BASE12, 0, 16 }, 50862306a36Sopenharmony_ci { "phy_serdes_ber_errors", -1, MII_BRCM_CORE_BASE13, 8, 8 }, 50962306a36Sopenharmony_ci { "phy_false_carrier_sense_errors", -1, MII_BRCM_CORE_BASE13, 0, 8 }, 51062306a36Sopenharmony_ci { "phy_local_rcvr_nok", -1, MII_BRCM_CORE_BASE14, 8, 8 }, 51162306a36Sopenharmony_ci { "phy_remote_rcv_nok", -1, MII_BRCM_CORE_BASE14, 0, 8 }, 51262306a36Sopenharmony_ci { "phy_lpi_count", MDIO_MMD_AN, BRCM_CL45VEN_EEE_LPI_CNT, 0, 16 }, 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ciint bcm_phy_get_sset_count(struct phy_device *phydev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci return ARRAY_SIZE(bcm_phy_hw_stats); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_civoid bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci unsigned int i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 52662306a36Sopenharmony_ci strscpy(data + i * ETH_GSTRING_LEN, 52762306a36Sopenharmony_ci bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_strings); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* Caller is supposed to provide appropriate storage for the library code to 53262306a36Sopenharmony_ci * access the shadow copy 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_cistatic u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 53562306a36Sopenharmony_ci unsigned int i) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 53862306a36Sopenharmony_ci int val; 53962306a36Sopenharmony_ci u64 ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (stat.devad < 0) 54262306a36Sopenharmony_ci val = phy_read(phydev, stat.reg); 54362306a36Sopenharmony_ci else 54462306a36Sopenharmony_ci val = phy_read_mmd(phydev, stat.devad, stat.reg); 54562306a36Sopenharmony_ci if (val < 0) { 54662306a36Sopenharmony_ci ret = U64_MAX; 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci val >>= stat.shift; 54962306a36Sopenharmony_ci val = val & ((1 << stat.bits) - 1); 55062306a36Sopenharmony_ci shadow[i] += val; 55162306a36Sopenharmony_ci ret = shadow[i]; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_civoid bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 55862306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci unsigned int i; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 56362306a36Sopenharmony_ci data[i] = bcm_phy_get_stat(phydev, shadow, i); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_stats); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_civoid bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci /* Reset R_CAL/RC_CAL Engine */ 57062306a36Sopenharmony_ci bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Disable Reset R_AL/RC_CAL Engine */ 57362306a36Sopenharmony_ci bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ciint bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci /* Increase VCO range to prevent unlocking problem of PLL at low 58062306a36Sopenharmony_ci * temp 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Change Ki to 011 */ 58562306a36Sopenharmony_ci bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Disable loading of TVCO buffer to bandgap, set bandgap trim 58862306a36Sopenharmony_ci * to 111 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Adjust bias current trim by -3 */ 59362306a36Sopenharmony_ci bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Switch to CORE_BASE1E */ 59662306a36Sopenharmony_ci phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci bcm_phy_r_rc_cal_reset(phydev); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* write AFE_RXCONFIG_0 */ 60162306a36Sopenharmony_ci bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* write AFE_RXCONFIG_1 */ 60462306a36Sopenharmony_ci bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* write AFE_RX_LP_COUNTER */ 60762306a36Sopenharmony_ci bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* write AFE_HPF_TRIM_OTHERS */ 61062306a36Sopenharmony_ci bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* write AFTE_TX_CONFIG */ 61362306a36Sopenharmony_ci bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciint bcm_phy_enable_jumbo(struct phy_device *phydev) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 62462306a36Sopenharmony_ci if (ret < 0) 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Enable extended length packet reception */ 62862306a36Sopenharmony_ci ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 62962306a36Sopenharmony_ci ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 63062306a36Sopenharmony_ci if (ret < 0) 63162306a36Sopenharmony_ci return ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Enable the elastic FIFO for raising the transmission limit from 63462306a36Sopenharmony_ci * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 63562306a36Sopenharmony_ci * latency. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_ci return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 64962306a36Sopenharmony_ci BCM54XX_ACCESS_MODE_LEGACY_EN); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci u16 mask, set; 65562306a36Sopenharmony_ci int ret; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Auto-negotiation must be enabled for cable diagnostics to work, but 65862306a36Sopenharmony_ci * don't advertise any capabilities. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 66162306a36Sopenharmony_ci phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 66262306a36Sopenharmony_ci phy_write(phydev, MII_CTRL1000, 0); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 66562306a36Sopenharmony_ci if (is_rdb) { 66662306a36Sopenharmony_ci ret = __bcm_phy_enable_legacy_access(phydev); 66762306a36Sopenharmony_ci if (ret) 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 67262306a36Sopenharmony_ci set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 67362306a36Sopenharmony_ci FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 67462306a36Sopenharmony_ci BCM54XX_ECD_CTRL_UNIT_CM); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciout: 67962306a36Sopenharmony_ci /* re-enable the RDB access even if there was an error */ 68062306a36Sopenharmony_ci if (is_rdb) 68162306a36Sopenharmony_ci ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int bcm_phy_cable_test_report_trans(int result) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci switch (result) { 69162306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_OK: 69262306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OK; 69362306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_OPEN: 69462306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 69562306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 69662306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 69762306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 69862306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 69962306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_INVALID: 70062306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_BUSY: 70162306a36Sopenharmony_ci default: 70262306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic bool bcm_phy_distance_valid(int result) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci switch (result) { 70962306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_OPEN: 71062306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 71162306a36Sopenharmony_ci case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 71262306a36Sopenharmony_ci return true; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci return false; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int bcm_phy_report_length(struct phy_device *phydev, int pair) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci int val; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci val = __bcm_phy_read_exp(phydev, 72262306a36Sopenharmony_ci BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 72362306a36Sopenharmony_ci if (val < 0) 72462306a36Sopenharmony_ci return val; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ethnl_cable_test_fault_length(phydev, pair, val); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 73562306a36Sopenharmony_ci bool *finished, bool is_rdb) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci int pair_a, pair_b, pair_c, pair_d, ret; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci *finished = false; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (is_rdb) { 74462306a36Sopenharmony_ci ret = __bcm_phy_enable_legacy_access(phydev); 74562306a36Sopenharmony_ci if (ret) 74662306a36Sopenharmony_ci goto out; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 75062306a36Sopenharmony_ci if (ret < 0) 75162306a36Sopenharmony_ci goto out; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 75462306a36Sopenharmony_ci ret = 0; 75562306a36Sopenharmony_ci goto out; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 75962306a36Sopenharmony_ci if (ret < 0) 76062306a36Sopenharmony_ci goto out; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 76362306a36Sopenharmony_ci pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 76462306a36Sopenharmony_ci pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 76562306a36Sopenharmony_ci pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 76862306a36Sopenharmony_ci bcm_phy_cable_test_report_trans(pair_a)); 76962306a36Sopenharmony_ci ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 77062306a36Sopenharmony_ci bcm_phy_cable_test_report_trans(pair_b)); 77162306a36Sopenharmony_ci ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 77262306a36Sopenharmony_ci bcm_phy_cable_test_report_trans(pair_c)); 77362306a36Sopenharmony_ci ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 77462306a36Sopenharmony_ci bcm_phy_cable_test_report_trans(pair_d)); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (bcm_phy_distance_valid(pair_a)) 77762306a36Sopenharmony_ci bcm_phy_report_length(phydev, 0); 77862306a36Sopenharmony_ci if (bcm_phy_distance_valid(pair_b)) 77962306a36Sopenharmony_ci bcm_phy_report_length(phydev, 1); 78062306a36Sopenharmony_ci if (bcm_phy_distance_valid(pair_c)) 78162306a36Sopenharmony_ci bcm_phy_report_length(phydev, 2); 78262306a36Sopenharmony_ci if (bcm_phy_distance_valid(pair_d)) 78362306a36Sopenharmony_ci bcm_phy_report_length(phydev, 3); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci ret = 0; 78662306a36Sopenharmony_ci *finished = true; 78762306a36Sopenharmony_ciout: 78862306a36Sopenharmony_ci /* re-enable the RDB access even if there was an error */ 78962306a36Sopenharmony_ci if (is_rdb) 79062306a36Sopenharmony_ci ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return ret; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ciint bcm_phy_cable_test_start(struct phy_device *phydev) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci return _bcm_phy_cable_test_start(phydev, false); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ciint bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci return _bcm_phy_cable_test_get_status(phydev, finished, false); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci/* We assume that all PHYs which support RDB access can be switched to legacy 81062306a36Sopenharmony_ci * mode. If, in the future, this is not true anymore, we have to re-implement 81162306a36Sopenharmony_ci * this with RDB access. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ciint bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci return _bcm_phy_cable_test_start(phydev, true); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ciint bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 82062306a36Sopenharmony_ci bool *finished) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci return _bcm_phy_cable_test_get_status(phydev, finished, true); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci#define BCM54XX_WOL_SUPPORTED_MASK (WAKE_UCAST | \ 82762306a36Sopenharmony_ci WAKE_MCAST | \ 82862306a36Sopenharmony_ci WAKE_BCAST | \ 82962306a36Sopenharmony_ci WAKE_MAGIC | \ 83062306a36Sopenharmony_ci WAKE_MAGICSECURE) 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciint bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci struct net_device *ndev = phydev->attached_dev; 83562306a36Sopenharmony_ci u8 da[ETH_ALEN], mask[ETH_ALEN]; 83662306a36Sopenharmony_ci unsigned int i; 83762306a36Sopenharmony_ci u16 ctl; 83862306a36Sopenharmony_ci int ret; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* Allow a MAC driver to play through its own Wake-on-LAN 84162306a36Sopenharmony_ci * implementation 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK) 84462306a36Sopenharmony_ci return -EOPNOTSUPP; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's 84762306a36Sopenharmony_ci * ethtool only supports 6, for now. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Clear previous interrupts */ 85262306a36Sopenharmony_ci ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); 85762306a36Sopenharmony_ci if (ret < 0) 85862306a36Sopenharmony_ci return ret; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ctl = ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (!wol->wolopts) { 86362306a36Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) 86462306a36Sopenharmony_ci disable_irq_wake(phydev->irq); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* Leave all interrupts disabled */ 86762306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 86862306a36Sopenharmony_ci BCM54XX_WOL_ALL_INTRS); 86962306a36Sopenharmony_ci if (ret < 0) 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Disable the global Wake-on-LAN enable bit */ 87362306a36Sopenharmony_ci ctl &= ~BCM54XX_WOL_EN; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Clear the previously configured mode and mask mode for Wake-on-LAN */ 87962306a36Sopenharmony_ci ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT); 88062306a36Sopenharmony_ci ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT); 88162306a36Sopenharmony_ci ctl &= ~BCM54XX_WOL_DIR_PKT_EN; 88262306a36Sopenharmony_ci ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* When using WAKE_MAGIC, we program the magic pattern filter to match 88562306a36Sopenharmony_ci * the device's MAC address and we accept any MAC DA in the Ethernet 88662306a36Sopenharmony_ci * frame. 88762306a36Sopenharmony_ci * 88862306a36Sopenharmony_ci * When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the 88962306a36Sopenharmony_ci * following: 89062306a36Sopenharmony_ci * - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match 89162306a36Sopenharmony_ci * - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care 89262306a36Sopenharmony_ci * - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * Note that the Broadcast MAC DA is inherently going to match the 89562306a36Sopenharmony_ci * multicast pattern being matched. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci memset(mask, 0, sizeof(mask)); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (wol->wolopts & WAKE_MCAST) { 90062306a36Sopenharmony_ci memset(da, 0, sizeof(da)); 90162306a36Sopenharmony_ci memset(mask, 0xff, sizeof(mask)); 90262306a36Sopenharmony_ci da[0] = 0x01; 90362306a36Sopenharmony_ci mask[0] = ~da[0]; 90462306a36Sopenharmony_ci } else { 90562306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) { 90662306a36Sopenharmony_ci ether_addr_copy(da, ndev->dev_addr); 90762306a36Sopenharmony_ci } else if (wol->wolopts & WAKE_BCAST) { 90862306a36Sopenharmony_ci eth_broadcast_addr(da); 90962306a36Sopenharmony_ci } else if (wol->wolopts & WAKE_MAGICSECURE) { 91062306a36Sopenharmony_ci ether_addr_copy(da, wol->sopass); 91162306a36Sopenharmony_ci } else if (wol->wolopts & WAKE_MAGIC) { 91262306a36Sopenharmony_ci memset(da, 0, sizeof(da)); 91362306a36Sopenharmony_ci memset(mask, 0xff, sizeof(mask)); 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci for (i = 0; i < ETH_ALEN / 2; i++) { 91862306a36Sopenharmony_ci if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { 91962306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, 92062306a36Sopenharmony_ci BCM54XX_WOL_MPD_DATA1(2 - i), 92162306a36Sopenharmony_ci ndev->dev_addr[i * 2] << 8 | 92262306a36Sopenharmony_ci ndev->dev_addr[i * 2 + 1]); 92362306a36Sopenharmony_ci if (ret < 0) 92462306a36Sopenharmony_ci return ret; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i), 92862306a36Sopenharmony_ci da[i * 2] << 8 | da[i * 2 + 1]); 92962306a36Sopenharmony_ci if (ret < 0) 93062306a36Sopenharmony_ci return ret; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i), 93362306a36Sopenharmony_ci mask[i * 2] << 8 | mask[i * 2 + 1]); 93462306a36Sopenharmony_ci if (ret) 93562306a36Sopenharmony_ci return ret; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGICSECURE) { 93962306a36Sopenharmony_ci ctl |= BCM54XX_WOL_SECKEY_OPT_6B << 94062306a36Sopenharmony_ci BCM54XX_WOL_SECKEY_OPT_SHIFT; 94162306a36Sopenharmony_ci ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT; 94262306a36Sopenharmony_ci ctl |= BCM54XX_WOL_MASK_MODE_DA_FF << 94362306a36Sopenharmony_ci BCM54XX_WOL_MASK_MODE_SHIFT; 94462306a36Sopenharmony_ci } else { 94562306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 94662306a36Sopenharmony_ci ctl |= BCM54XX_WOL_MODE_SINGLE_MPD; 94762306a36Sopenharmony_ci else 94862306a36Sopenharmony_ci ctl |= BCM54XX_WOL_DIR_PKT_EN; 94962306a36Sopenharmony_ci ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY << 95062306a36Sopenharmony_ci BCM54XX_WOL_MASK_MODE_SHIFT; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Globally enable Wake-on-LAN */ 95462306a36Sopenharmony_ci ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl); 95762306a36Sopenharmony_ci if (ret < 0) 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* Enable WOL interrupt on LED4 */ 96162306a36Sopenharmony_ci ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL); 96262306a36Sopenharmony_ci if (ret < 0) 96362306a36Sopenharmony_ci return ret; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ret |= BCM54XX_LED4_SEL_INTR; 96662306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret); 96762306a36Sopenharmony_ci if (ret < 0) 96862306a36Sopenharmony_ci return ret; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Enable all Wake-on-LAN interrupt sources */ 97162306a36Sopenharmony_ci ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0); 97262306a36Sopenharmony_ci if (ret < 0) 97362306a36Sopenharmony_ci return ret; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) 97662306a36Sopenharmony_ci enable_irq_wake(phydev->irq); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_set_wol); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_civoid bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct net_device *ndev = phydev->attached_dev; 98562306a36Sopenharmony_ci u8 da[ETH_ALEN]; 98662306a36Sopenharmony_ci unsigned int i; 98762306a36Sopenharmony_ci int ret; 98862306a36Sopenharmony_ci u16 ctl; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci wol->supported = BCM54XX_WOL_SUPPORTED_MASK; 99162306a36Sopenharmony_ci wol->wolopts = 0; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL); 99462306a36Sopenharmony_ci if (ret < 0) 99562306a36Sopenharmony_ci return; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ctl = ret; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (!(ctl & BCM54XX_WOL_EN)) 100062306a36Sopenharmony_ci return; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci for (i = 0; i < sizeof(da) / 2; i++) { 100362306a36Sopenharmony_ci ret = bcm_phy_read_exp(phydev, 100462306a36Sopenharmony_ci BCM54XX_WOL_MPD_DATA2(2 - i)); 100562306a36Sopenharmony_ci if (ret < 0) 100662306a36Sopenharmony_ci return; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci da[i * 2] = ret >> 8; 100962306a36Sopenharmony_ci da[i * 2 + 1] = ret & 0xff; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (ctl & BCM54XX_WOL_DIR_PKT_EN) { 101362306a36Sopenharmony_ci if (is_broadcast_ether_addr(da)) 101462306a36Sopenharmony_ci wol->wolopts |= WAKE_BCAST; 101562306a36Sopenharmony_ci else if (is_multicast_ether_addr(da)) 101662306a36Sopenharmony_ci wol->wolopts |= WAKE_MCAST; 101762306a36Sopenharmony_ci else if (ether_addr_equal(da, ndev->dev_addr)) 101862306a36Sopenharmony_ci wol->wolopts |= WAKE_UCAST; 101962306a36Sopenharmony_ci } else { 102062306a36Sopenharmony_ci ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK; 102162306a36Sopenharmony_ci switch (ctl) { 102262306a36Sopenharmony_ci case BCM54XX_WOL_MODE_SINGLE_MPD: 102362306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci case BCM54XX_WOL_MODE_SINGLE_MPDSEC: 102662306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGICSECURE; 102762306a36Sopenharmony_ci memcpy(wol->sopass, da, sizeof(da)); 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci default: 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_wol); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ciirqreturn_t bcm_phy_wol_isr(int irq, void *dev_id) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci return IRQ_HANDLED; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_wol_isr); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ciint bcm_phy_led_brightness_set(struct phy_device *phydev, 104362306a36Sopenharmony_ci u8 index, enum led_brightness value) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci u8 led_num; 104662306a36Sopenharmony_ci int ret; 104762306a36Sopenharmony_ci u16 reg; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (index >= 4) 105062306a36Sopenharmony_ci return -EINVAL; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* Two LEDS per register */ 105362306a36Sopenharmony_ci led_num = index % 2; 105462306a36Sopenharmony_ci reg = index >= 2 ? BCM54XX_SHD_LEDS2 : BCM54XX_SHD_LEDS1; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ret = bcm_phy_read_shadow(phydev, reg); 105762306a36Sopenharmony_ci if (ret < 0) 105862306a36Sopenharmony_ci return ret; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci ret &= ~(BCM_LED_SRC_MASK << BCM54XX_SHD_LEDS_SHIFT(led_num)); 106162306a36Sopenharmony_ci if (value == LED_OFF) 106262306a36Sopenharmony_ci ret |= BCM_LED_SRC_OFF << BCM54XX_SHD_LEDS_SHIFT(led_num); 106362306a36Sopenharmony_ci else 106462306a36Sopenharmony_ci ret |= BCM_LED_SRC_ON << BCM54XX_SHD_LEDS_SHIFT(led_num); 106562306a36Sopenharmony_ci return bcm_phy_write_shadow(phydev, reg, ret); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom PHY Library"); 107062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 107162306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation"); 1072