1*6937602eSMichael Walle // SPDX-License-Identifier: GPL-2.0+ 2*6937602eSMichael Walle /* Broadcom BCM54140 Quad SGMII/QSGMII Copper/Fiber Gigabit PHY 3*6937602eSMichael Walle * 4*6937602eSMichael Walle * Copyright (c) 2020 Michael Walle <michael@walle.cc> 5*6937602eSMichael Walle */ 6*6937602eSMichael Walle 7*6937602eSMichael Walle #include <linux/bitfield.h> 8*6937602eSMichael Walle #include <linux/brcmphy.h> 9*6937602eSMichael Walle #include <linux/module.h> 10*6937602eSMichael Walle #include <linux/phy.h> 11*6937602eSMichael Walle 12*6937602eSMichael Walle #include "bcm-phy-lib.h" 13*6937602eSMichael Walle 14*6937602eSMichael Walle /* RDB per-port registers 15*6937602eSMichael Walle */ 16*6937602eSMichael Walle #define BCM54140_RDB_ISR 0x00a /* interrupt status */ 17*6937602eSMichael Walle #define BCM54140_RDB_IMR 0x00b /* interrupt mask */ 18*6937602eSMichael Walle #define BCM54140_RDB_INT_LINK BIT(1) /* link status changed */ 19*6937602eSMichael Walle #define BCM54140_RDB_INT_SPEED BIT(2) /* link speed change */ 20*6937602eSMichael Walle #define BCM54140_RDB_INT_DUPLEX BIT(3) /* duplex mode changed */ 21*6937602eSMichael Walle #define BCM54140_RDB_SPARE1 0x012 /* spare control 1 */ 22*6937602eSMichael Walle #define BCM54140_RDB_SPARE1_LSLM BIT(2) /* link speed LED mode */ 23*6937602eSMichael Walle #define BCM54140_RDB_SPARE2 0x014 /* spare control 2 */ 24*6937602eSMichael Walle #define BCM54140_RDB_SPARE2_WS_RTRY_DIS BIT(8) /* wirespeed retry disable */ 25*6937602eSMichael Walle #define BCM54140_RDB_SPARE2_WS_RTRY_LIMIT GENMASK(4, 2) /* retry limit */ 26*6937602eSMichael Walle #define BCM54140_RDB_SPARE3 0x015 /* spare control 3 */ 27*6937602eSMichael Walle #define BCM54140_RDB_SPARE3_BIT0 BIT(0) 28*6937602eSMichael Walle #define BCM54140_RDB_LED_CTRL 0x019 /* LED control */ 29*6937602eSMichael Walle #define BCM54140_RDB_LED_CTRL_ACTLINK0 BIT(4) 30*6937602eSMichael Walle #define BCM54140_RDB_LED_CTRL_ACTLINK1 BIT(8) 31*6937602eSMichael Walle #define BCM54140_RDB_C_APWR 0x01a /* auto power down control */ 32*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_SINGLE_PULSE BIT(8) /* single pulse */ 33*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_APD_MODE_DIS 0 /* ADP disable */ 34*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_APD_MODE_EN 1 /* ADP enable */ 35*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_APD_MODE_DIS2 2 /* ADP disable */ 36*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG 3 /* ADP enable w/ aneg */ 37*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_APD_MODE_MASK GENMASK(6, 5) 38*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_SLP_TIM_MASK BIT(4)/* sleep timer */ 39*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_SLP_TIM_2_7 0 /* 2.7s */ 40*6937602eSMichael Walle #define BCM54140_RDB_C_APWR_SLP_TIM_5_4 1 /* 5.4s */ 41*6937602eSMichael Walle #define BCM54140_RDB_C_PWR 0x02a /* copper power control */ 42*6937602eSMichael Walle #define BCM54140_RDB_C_PWR_ISOLATE BIT(5) /* super isolate mode */ 43*6937602eSMichael Walle #define BCM54140_RDB_C_MISC_CTRL 0x02f /* misc copper control */ 44*6937602eSMichael Walle #define BCM54140_RDB_C_MISC_CTRL_WS_EN BIT(4) /* wirespeed enable */ 45*6937602eSMichael Walle 46*6937602eSMichael Walle /* RDB global registers 47*6937602eSMichael Walle */ 48*6937602eSMichael Walle #define BCM54140_RDB_TOP_IMR 0x82d /* interrupt mask */ 49*6937602eSMichael Walle #define BCM54140_RDB_TOP_IMR_PORT0 BIT(4) 50*6937602eSMichael Walle #define BCM54140_RDB_TOP_IMR_PORT1 BIT(5) 51*6937602eSMichael Walle #define BCM54140_RDB_TOP_IMR_PORT2 BIT(6) 52*6937602eSMichael Walle #define BCM54140_RDB_TOP_IMR_PORT3 BIT(7) 53*6937602eSMichael Walle 54*6937602eSMichael Walle #define BCM54140_DEFAULT_DOWNSHIFT 5 55*6937602eSMichael Walle #define BCM54140_MAX_DOWNSHIFT 9 56*6937602eSMichael Walle 57*6937602eSMichael Walle struct bcm54140_priv { 58*6937602eSMichael Walle int port; 59*6937602eSMichael Walle int base_addr; 60*6937602eSMichael Walle }; 61*6937602eSMichael Walle 62*6937602eSMichael Walle static int bcm54140_base_read_rdb(struct phy_device *phydev, u16 rdb) 63*6937602eSMichael Walle { 64*6937602eSMichael Walle struct bcm54140_priv *priv = phydev->priv; 65*6937602eSMichael Walle struct mii_bus *bus = phydev->mdio.bus; 66*6937602eSMichael Walle int ret; 67*6937602eSMichael Walle 68*6937602eSMichael Walle mutex_lock(&bus->mdio_lock); 69*6937602eSMichael Walle ret = __mdiobus_write(bus, priv->base_addr, MII_BCM54XX_RDB_ADDR, rdb); 70*6937602eSMichael Walle if (ret < 0) 71*6937602eSMichael Walle goto out; 72*6937602eSMichael Walle 73*6937602eSMichael Walle ret = __mdiobus_read(bus, priv->base_addr, MII_BCM54XX_RDB_DATA); 74*6937602eSMichael Walle 75*6937602eSMichael Walle out: 76*6937602eSMichael Walle mutex_unlock(&bus->mdio_lock); 77*6937602eSMichael Walle return ret; 78*6937602eSMichael Walle } 79*6937602eSMichael Walle 80*6937602eSMichael Walle static int bcm54140_base_write_rdb(struct phy_device *phydev, 81*6937602eSMichael Walle u16 rdb, u16 val) 82*6937602eSMichael Walle { 83*6937602eSMichael Walle struct bcm54140_priv *priv = phydev->priv; 84*6937602eSMichael Walle struct mii_bus *bus = phydev->mdio.bus; 85*6937602eSMichael Walle int ret; 86*6937602eSMichael Walle 87*6937602eSMichael Walle mutex_lock(&bus->mdio_lock); 88*6937602eSMichael Walle ret = __mdiobus_write(bus, priv->base_addr, MII_BCM54XX_RDB_ADDR, rdb); 89*6937602eSMichael Walle if (ret < 0) 90*6937602eSMichael Walle goto out; 91*6937602eSMichael Walle 92*6937602eSMichael Walle ret = __mdiobus_write(bus, priv->base_addr, MII_BCM54XX_RDB_DATA, val); 93*6937602eSMichael Walle 94*6937602eSMichael Walle out: 95*6937602eSMichael Walle mutex_unlock(&bus->mdio_lock); 96*6937602eSMichael Walle return ret; 97*6937602eSMichael Walle } 98*6937602eSMichael Walle 99*6937602eSMichael Walle /* Under some circumstances a core PLL may not lock, this will then prevent 100*6937602eSMichael Walle * a successful link establishment. Restart the PLL after the voltages are 101*6937602eSMichael Walle * stable to workaround this issue. 102*6937602eSMichael Walle */ 103*6937602eSMichael Walle static int bcm54140_b0_workaround(struct phy_device *phydev) 104*6937602eSMichael Walle { 105*6937602eSMichael Walle int spare3; 106*6937602eSMichael Walle int ret; 107*6937602eSMichael Walle 108*6937602eSMichael Walle spare3 = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE3); 109*6937602eSMichael Walle if (spare3 < 0) 110*6937602eSMichael Walle return spare3; 111*6937602eSMichael Walle 112*6937602eSMichael Walle spare3 &= ~BCM54140_RDB_SPARE3_BIT0; 113*6937602eSMichael Walle 114*6937602eSMichael Walle ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3); 115*6937602eSMichael Walle if (ret) 116*6937602eSMichael Walle return ret; 117*6937602eSMichael Walle 118*6937602eSMichael Walle ret = phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN); 119*6937602eSMichael Walle if (ret) 120*6937602eSMichael Walle return ret; 121*6937602eSMichael Walle 122*6937602eSMichael Walle ret = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0); 123*6937602eSMichael Walle if (ret) 124*6937602eSMichael Walle return ret; 125*6937602eSMichael Walle 126*6937602eSMichael Walle spare3 |= BCM54140_RDB_SPARE3_BIT0; 127*6937602eSMichael Walle 128*6937602eSMichael Walle return bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3); 129*6937602eSMichael Walle } 130*6937602eSMichael Walle 131*6937602eSMichael Walle /* The BCM54140 is a quad PHY where only the first port has access to the 132*6937602eSMichael Walle * global register. Thus we need to find out its PHY address. 133*6937602eSMichael Walle * 134*6937602eSMichael Walle */ 135*6937602eSMichael Walle static int bcm54140_get_base_addr_and_port(struct phy_device *phydev) 136*6937602eSMichael Walle { 137*6937602eSMichael Walle struct bcm54140_priv *priv = phydev->priv; 138*6937602eSMichael Walle struct mii_bus *bus = phydev->mdio.bus; 139*6937602eSMichael Walle int addr, min_addr, max_addr; 140*6937602eSMichael Walle int step = 1; 141*6937602eSMichael Walle u32 phy_id; 142*6937602eSMichael Walle int tmp; 143*6937602eSMichael Walle 144*6937602eSMichael Walle min_addr = phydev->mdio.addr; 145*6937602eSMichael Walle max_addr = phydev->mdio.addr; 146*6937602eSMichael Walle addr = phydev->mdio.addr; 147*6937602eSMichael Walle 148*6937602eSMichael Walle /* We scan forward and backwards and look for PHYs which have the 149*6937602eSMichael Walle * same phy_id like we do. Step 1 will scan forward, step 2 150*6937602eSMichael Walle * backwards. Once we are finished, we have a min_addr and 151*6937602eSMichael Walle * max_addr which resembles the range of PHY addresses of the same 152*6937602eSMichael Walle * type of PHY. There is one caveat; there may be many PHYs of 153*6937602eSMichael Walle * the same type, but we know that each PHY takes exactly 4 154*6937602eSMichael Walle * consecutive addresses. Therefore we can deduce our offset 155*6937602eSMichael Walle * to the base address of this quad PHY. 156*6937602eSMichael Walle */ 157*6937602eSMichael Walle 158*6937602eSMichael Walle while (1) { 159*6937602eSMichael Walle if (step == 3) { 160*6937602eSMichael Walle break; 161*6937602eSMichael Walle } else if (step == 1) { 162*6937602eSMichael Walle max_addr = addr; 163*6937602eSMichael Walle addr++; 164*6937602eSMichael Walle } else { 165*6937602eSMichael Walle min_addr = addr; 166*6937602eSMichael Walle addr--; 167*6937602eSMichael Walle } 168*6937602eSMichael Walle 169*6937602eSMichael Walle if (addr < 0 || addr >= PHY_MAX_ADDR) { 170*6937602eSMichael Walle addr = phydev->mdio.addr; 171*6937602eSMichael Walle step++; 172*6937602eSMichael Walle continue; 173*6937602eSMichael Walle } 174*6937602eSMichael Walle 175*6937602eSMichael Walle /* read the PHY id */ 176*6937602eSMichael Walle tmp = mdiobus_read(bus, addr, MII_PHYSID1); 177*6937602eSMichael Walle if (tmp < 0) 178*6937602eSMichael Walle return tmp; 179*6937602eSMichael Walle phy_id = tmp << 16; 180*6937602eSMichael Walle tmp = mdiobus_read(bus, addr, MII_PHYSID2); 181*6937602eSMichael Walle if (tmp < 0) 182*6937602eSMichael Walle return tmp; 183*6937602eSMichael Walle phy_id |= tmp; 184*6937602eSMichael Walle 185*6937602eSMichael Walle /* see if it is still the same PHY */ 186*6937602eSMichael Walle if ((phy_id & phydev->drv->phy_id_mask) != 187*6937602eSMichael Walle (phydev->drv->phy_id & phydev->drv->phy_id_mask)) { 188*6937602eSMichael Walle addr = phydev->mdio.addr; 189*6937602eSMichael Walle step++; 190*6937602eSMichael Walle } 191*6937602eSMichael Walle } 192*6937602eSMichael Walle 193*6937602eSMichael Walle /* The range we get should be a multiple of four. Please note that both 194*6937602eSMichael Walle * the min_addr and max_addr are inclusive. So we have to add one if we 195*6937602eSMichael Walle * subtract them. 196*6937602eSMichael Walle */ 197*6937602eSMichael Walle if ((max_addr - min_addr + 1) % 4) { 198*6937602eSMichael Walle dev_err(&phydev->mdio.dev, 199*6937602eSMichael Walle "Detected Quad PHY IDs %d..%d doesn't make sense.\n", 200*6937602eSMichael Walle min_addr, max_addr); 201*6937602eSMichael Walle return -EINVAL; 202*6937602eSMichael Walle } 203*6937602eSMichael Walle 204*6937602eSMichael Walle priv->port = (phydev->mdio.addr - min_addr) % 4; 205*6937602eSMichael Walle priv->base_addr = phydev->mdio.addr - priv->port; 206*6937602eSMichael Walle 207*6937602eSMichael Walle return 0; 208*6937602eSMichael Walle } 209*6937602eSMichael Walle 210*6937602eSMichael Walle static int bcm54140_probe(struct phy_device *phydev) 211*6937602eSMichael Walle { 212*6937602eSMichael Walle struct bcm54140_priv *priv; 213*6937602eSMichael Walle int ret; 214*6937602eSMichael Walle 215*6937602eSMichael Walle priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 216*6937602eSMichael Walle if (!priv) 217*6937602eSMichael Walle return -ENOMEM; 218*6937602eSMichael Walle 219*6937602eSMichael Walle phydev->priv = priv; 220*6937602eSMichael Walle 221*6937602eSMichael Walle ret = bcm54140_get_base_addr_and_port(phydev); 222*6937602eSMichael Walle if (ret) 223*6937602eSMichael Walle return ret; 224*6937602eSMichael Walle 225*6937602eSMichael Walle phydev_dbg(phydev, "probed (port %d, base PHY address %d)\n", 226*6937602eSMichael Walle priv->port, priv->base_addr); 227*6937602eSMichael Walle 228*6937602eSMichael Walle return 0; 229*6937602eSMichael Walle } 230*6937602eSMichael Walle 231*6937602eSMichael Walle static int bcm54140_config_init(struct phy_device *phydev) 232*6937602eSMichael Walle { 233*6937602eSMichael Walle u16 reg = 0xffff; 234*6937602eSMichael Walle int ret; 235*6937602eSMichael Walle 236*6937602eSMichael Walle /* Apply hardware errata */ 237*6937602eSMichael Walle ret = bcm54140_b0_workaround(phydev); 238*6937602eSMichael Walle if (ret) 239*6937602eSMichael Walle return ret; 240*6937602eSMichael Walle 241*6937602eSMichael Walle /* Unmask events we are interested in. */ 242*6937602eSMichael Walle reg &= ~(BCM54140_RDB_INT_DUPLEX | 243*6937602eSMichael Walle BCM54140_RDB_INT_SPEED | 244*6937602eSMichael Walle BCM54140_RDB_INT_LINK); 245*6937602eSMichael Walle ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_IMR, reg); 246*6937602eSMichael Walle if (ret) 247*6937602eSMichael Walle return ret; 248*6937602eSMichael Walle 249*6937602eSMichael Walle /* LED1=LINKSPD[1], LED2=LINKSPD[2], LED3=LINK/ACTIVITY */ 250*6937602eSMichael Walle ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE1, 251*6937602eSMichael Walle 0, BCM54140_RDB_SPARE1_LSLM); 252*6937602eSMichael Walle if (ret) 253*6937602eSMichael Walle return ret; 254*6937602eSMichael Walle 255*6937602eSMichael Walle ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_LED_CTRL, 256*6937602eSMichael Walle 0, BCM54140_RDB_LED_CTRL_ACTLINK0); 257*6937602eSMichael Walle if (ret) 258*6937602eSMichael Walle return ret; 259*6937602eSMichael Walle 260*6937602eSMichael Walle /* disable super isolate mode */ 261*6937602eSMichael Walle return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_PWR, 262*6937602eSMichael Walle BCM54140_RDB_C_PWR_ISOLATE, 0); 263*6937602eSMichael Walle } 264*6937602eSMichael Walle 265*6937602eSMichael Walle int bcm54140_did_interrupt(struct phy_device *phydev) 266*6937602eSMichael Walle { 267*6937602eSMichael Walle int ret; 268*6937602eSMichael Walle 269*6937602eSMichael Walle ret = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR); 270*6937602eSMichael Walle 271*6937602eSMichael Walle return (ret < 0) ? 0 : ret; 272*6937602eSMichael Walle } 273*6937602eSMichael Walle 274*6937602eSMichael Walle int bcm54140_ack_intr(struct phy_device *phydev) 275*6937602eSMichael Walle { 276*6937602eSMichael Walle int reg; 277*6937602eSMichael Walle 278*6937602eSMichael Walle /* clear pending interrupts */ 279*6937602eSMichael Walle reg = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR); 280*6937602eSMichael Walle if (reg < 0) 281*6937602eSMichael Walle return reg; 282*6937602eSMichael Walle 283*6937602eSMichael Walle return 0; 284*6937602eSMichael Walle } 285*6937602eSMichael Walle 286*6937602eSMichael Walle int bcm54140_config_intr(struct phy_device *phydev) 287*6937602eSMichael Walle { 288*6937602eSMichael Walle struct bcm54140_priv *priv = phydev->priv; 289*6937602eSMichael Walle static const u16 port_to_imr_bit[] = { 290*6937602eSMichael Walle BCM54140_RDB_TOP_IMR_PORT0, BCM54140_RDB_TOP_IMR_PORT1, 291*6937602eSMichael Walle BCM54140_RDB_TOP_IMR_PORT2, BCM54140_RDB_TOP_IMR_PORT3, 292*6937602eSMichael Walle }; 293*6937602eSMichael Walle int reg; 294*6937602eSMichael Walle 295*6937602eSMichael Walle if (priv->port >= ARRAY_SIZE(port_to_imr_bit)) 296*6937602eSMichael Walle return -EINVAL; 297*6937602eSMichael Walle 298*6937602eSMichael Walle reg = bcm54140_base_read_rdb(phydev, BCM54140_RDB_TOP_IMR); 299*6937602eSMichael Walle if (reg < 0) 300*6937602eSMichael Walle return reg; 301*6937602eSMichael Walle 302*6937602eSMichael Walle if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 303*6937602eSMichael Walle reg &= ~port_to_imr_bit[priv->port]; 304*6937602eSMichael Walle else 305*6937602eSMichael Walle reg |= port_to_imr_bit[priv->port]; 306*6937602eSMichael Walle 307*6937602eSMichael Walle return bcm54140_base_write_rdb(phydev, BCM54140_RDB_TOP_IMR, reg); 308*6937602eSMichael Walle } 309*6937602eSMichael Walle 310*6937602eSMichael Walle static int bcm54140_get_downshift(struct phy_device *phydev, u8 *data) 311*6937602eSMichael Walle { 312*6937602eSMichael Walle int val; 313*6937602eSMichael Walle 314*6937602eSMichael Walle val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_MISC_CTRL); 315*6937602eSMichael Walle if (val < 0) 316*6937602eSMichael Walle return val; 317*6937602eSMichael Walle 318*6937602eSMichael Walle if (!(val & BCM54140_RDB_C_MISC_CTRL_WS_EN)) { 319*6937602eSMichael Walle *data = DOWNSHIFT_DEV_DISABLE; 320*6937602eSMichael Walle return 0; 321*6937602eSMichael Walle } 322*6937602eSMichael Walle 323*6937602eSMichael Walle val = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE2); 324*6937602eSMichael Walle if (val < 0) 325*6937602eSMichael Walle return val; 326*6937602eSMichael Walle 327*6937602eSMichael Walle if (val & BCM54140_RDB_SPARE2_WS_RTRY_DIS) 328*6937602eSMichael Walle *data = 1; 329*6937602eSMichael Walle else 330*6937602eSMichael Walle *data = FIELD_GET(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, val) + 2; 331*6937602eSMichael Walle 332*6937602eSMichael Walle return 0; 333*6937602eSMichael Walle } 334*6937602eSMichael Walle 335*6937602eSMichael Walle static int bcm54140_set_downshift(struct phy_device *phydev, u8 cnt) 336*6937602eSMichael Walle { 337*6937602eSMichael Walle u16 mask, set; 338*6937602eSMichael Walle int ret; 339*6937602eSMichael Walle 340*6937602eSMichael Walle if (cnt > BCM54140_MAX_DOWNSHIFT && cnt != DOWNSHIFT_DEV_DEFAULT_COUNT) 341*6937602eSMichael Walle return -EINVAL; 342*6937602eSMichael Walle 343*6937602eSMichael Walle if (!cnt) 344*6937602eSMichael Walle return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL, 345*6937602eSMichael Walle BCM54140_RDB_C_MISC_CTRL_WS_EN, 0); 346*6937602eSMichael Walle 347*6937602eSMichael Walle if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT) 348*6937602eSMichael Walle cnt = BCM54140_DEFAULT_DOWNSHIFT; 349*6937602eSMichael Walle 350*6937602eSMichael Walle if (cnt == 1) { 351*6937602eSMichael Walle mask = 0; 352*6937602eSMichael Walle set = BCM54140_RDB_SPARE2_WS_RTRY_DIS; 353*6937602eSMichael Walle } else { 354*6937602eSMichael Walle mask = BCM54140_RDB_SPARE2_WS_RTRY_DIS; 355*6937602eSMichael Walle mask |= BCM54140_RDB_SPARE2_WS_RTRY_LIMIT; 356*6937602eSMichael Walle set = FIELD_PREP(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, cnt - 2); 357*6937602eSMichael Walle } 358*6937602eSMichael Walle ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE2, 359*6937602eSMichael Walle mask, set); 360*6937602eSMichael Walle if (ret) 361*6937602eSMichael Walle return ret; 362*6937602eSMichael Walle 363*6937602eSMichael Walle return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL, 364*6937602eSMichael Walle 0, BCM54140_RDB_C_MISC_CTRL_WS_EN); 365*6937602eSMichael Walle } 366*6937602eSMichael Walle 367*6937602eSMichael Walle static int bcm54140_get_edpd(struct phy_device *phydev, u16 *tx_interval) 368*6937602eSMichael Walle { 369*6937602eSMichael Walle int val; 370*6937602eSMichael Walle 371*6937602eSMichael Walle val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_APWR); 372*6937602eSMichael Walle if (val < 0) 373*6937602eSMichael Walle return val; 374*6937602eSMichael Walle 375*6937602eSMichael Walle switch (FIELD_GET(BCM54140_RDB_C_APWR_APD_MODE_MASK, val)) { 376*6937602eSMichael Walle case BCM54140_RDB_C_APWR_APD_MODE_DIS: 377*6937602eSMichael Walle case BCM54140_RDB_C_APWR_APD_MODE_DIS2: 378*6937602eSMichael Walle *tx_interval = ETHTOOL_PHY_EDPD_DISABLE; 379*6937602eSMichael Walle break; 380*6937602eSMichael Walle case BCM54140_RDB_C_APWR_APD_MODE_EN: 381*6937602eSMichael Walle case BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG: 382*6937602eSMichael Walle switch (FIELD_GET(BCM54140_RDB_C_APWR_SLP_TIM_MASK, val)) { 383*6937602eSMichael Walle case BCM54140_RDB_C_APWR_SLP_TIM_2_7: 384*6937602eSMichael Walle *tx_interval = 2700; 385*6937602eSMichael Walle break; 386*6937602eSMichael Walle case BCM54140_RDB_C_APWR_SLP_TIM_5_4: 387*6937602eSMichael Walle *tx_interval = 5400; 388*6937602eSMichael Walle break; 389*6937602eSMichael Walle } 390*6937602eSMichael Walle } 391*6937602eSMichael Walle 392*6937602eSMichael Walle return 0; 393*6937602eSMichael Walle } 394*6937602eSMichael Walle 395*6937602eSMichael Walle static int bcm54140_set_edpd(struct phy_device *phydev, u16 tx_interval) 396*6937602eSMichael Walle { 397*6937602eSMichael Walle u16 mask, set; 398*6937602eSMichael Walle 399*6937602eSMichael Walle mask = BCM54140_RDB_C_APWR_APD_MODE_MASK; 400*6937602eSMichael Walle if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE) 401*6937602eSMichael Walle set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK, 402*6937602eSMichael Walle BCM54140_RDB_C_APWR_APD_MODE_DIS); 403*6937602eSMichael Walle else 404*6937602eSMichael Walle set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK, 405*6937602eSMichael Walle BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG); 406*6937602eSMichael Walle 407*6937602eSMichael Walle /* enable single pulse mode */ 408*6937602eSMichael Walle set |= BCM54140_RDB_C_APWR_SINGLE_PULSE; 409*6937602eSMichael Walle 410*6937602eSMichael Walle /* set sleep timer */ 411*6937602eSMichael Walle mask |= BCM54140_RDB_C_APWR_SLP_TIM_MASK; 412*6937602eSMichael Walle switch (tx_interval) { 413*6937602eSMichael Walle case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: 414*6937602eSMichael Walle case ETHTOOL_PHY_EDPD_DISABLE: 415*6937602eSMichael Walle case 2700: 416*6937602eSMichael Walle set |= BCM54140_RDB_C_APWR_SLP_TIM_2_7; 417*6937602eSMichael Walle break; 418*6937602eSMichael Walle case 5400: 419*6937602eSMichael Walle set |= BCM54140_RDB_C_APWR_SLP_TIM_5_4; 420*6937602eSMichael Walle break; 421*6937602eSMichael Walle default: 422*6937602eSMichael Walle return -EINVAL; 423*6937602eSMichael Walle } 424*6937602eSMichael Walle 425*6937602eSMichael Walle return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_APWR, mask, set); 426*6937602eSMichael Walle } 427*6937602eSMichael Walle 428*6937602eSMichael Walle static int bcm54140_get_tunable(struct phy_device *phydev, 429*6937602eSMichael Walle struct ethtool_tunable *tuna, void *data) 430*6937602eSMichael Walle { 431*6937602eSMichael Walle switch (tuna->id) { 432*6937602eSMichael Walle case ETHTOOL_PHY_DOWNSHIFT: 433*6937602eSMichael Walle return bcm54140_get_downshift(phydev, data); 434*6937602eSMichael Walle case ETHTOOL_PHY_EDPD: 435*6937602eSMichael Walle return bcm54140_get_edpd(phydev, data); 436*6937602eSMichael Walle default: 437*6937602eSMichael Walle return -EOPNOTSUPP; 438*6937602eSMichael Walle } 439*6937602eSMichael Walle } 440*6937602eSMichael Walle 441*6937602eSMichael Walle static int bcm54140_set_tunable(struct phy_device *phydev, 442*6937602eSMichael Walle struct ethtool_tunable *tuna, const void *data) 443*6937602eSMichael Walle { 444*6937602eSMichael Walle switch (tuna->id) { 445*6937602eSMichael Walle case ETHTOOL_PHY_DOWNSHIFT: 446*6937602eSMichael Walle return bcm54140_set_downshift(phydev, *(const u8 *)data); 447*6937602eSMichael Walle case ETHTOOL_PHY_EDPD: 448*6937602eSMichael Walle return bcm54140_set_edpd(phydev, *(const u16 *)data); 449*6937602eSMichael Walle default: 450*6937602eSMichael Walle return -EOPNOTSUPP; 451*6937602eSMichael Walle } 452*6937602eSMichael Walle } 453*6937602eSMichael Walle 454*6937602eSMichael Walle static struct phy_driver bcm54140_drivers[] = { 455*6937602eSMichael Walle { 456*6937602eSMichael Walle .phy_id = PHY_ID_BCM54140, 457*6937602eSMichael Walle .phy_id_mask = 0xfffffff0, 458*6937602eSMichael Walle .name = "Broadcom BCM54140", 459*6937602eSMichael Walle .features = PHY_GBIT_FEATURES, 460*6937602eSMichael Walle .config_init = bcm54140_config_init, 461*6937602eSMichael Walle .did_interrupt = bcm54140_did_interrupt, 462*6937602eSMichael Walle .ack_interrupt = bcm54140_ack_intr, 463*6937602eSMichael Walle .config_intr = bcm54140_config_intr, 464*6937602eSMichael Walle .probe = bcm54140_probe, 465*6937602eSMichael Walle .suspend = genphy_suspend, 466*6937602eSMichael Walle .resume = genphy_resume, 467*6937602eSMichael Walle .get_tunable = bcm54140_get_tunable, 468*6937602eSMichael Walle .set_tunable = bcm54140_set_tunable, 469*6937602eSMichael Walle }, 470*6937602eSMichael Walle }; 471*6937602eSMichael Walle module_phy_driver(bcm54140_drivers); 472*6937602eSMichael Walle 473*6937602eSMichael Walle static struct mdio_device_id __maybe_unused bcm54140_tbl[] = { 474*6937602eSMichael Walle { PHY_ID_BCM54140, 0xfffffff0 }, 475*6937602eSMichael Walle { } 476*6937602eSMichael Walle }; 477*6937602eSMichael Walle 478*6937602eSMichael Walle MODULE_AUTHOR("Michael Walle"); 479*6937602eSMichael Walle MODULE_DESCRIPTION("Broadcom BCM54140 PHY driver"); 480*6937602eSMichael Walle MODULE_DEVICE_TABLE(mdio, bcm54140_tbl); 481*6937602eSMichael Walle MODULE_LICENSE("GPL"); 482