1187 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2015-2017 Broadcom4 */5 6#include "bcm-phy-lib.h"7#include <linux/bitfield.h>8#include <linux/brcmphy.h>9#include <linux/etherdevice.h>10#include <linux/export.h>11#include <linux/mdio.h>12#include <linux/module.h>13#include <linux/phy.h>14#include <linux/ethtool.h>15#include <linux/ethtool_netlink.h>16#include <linux/netdevice.h>17 18#define MII_BCM_CHANNEL_WIDTH 0x200019#define BCM_CL45VEN_EEE_ADV 0x3c20 21int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)22{23 int rc;24 25 rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);26 if (rc < 0)27 return rc;28 29 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val);30}31EXPORT_SYMBOL_GPL(__bcm_phy_write_exp);32 33int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)34{35 int rc;36 37 phy_lock_mdio_bus(phydev);38 rc = __bcm_phy_write_exp(phydev, reg, val);39 phy_unlock_mdio_bus(phydev);40 41 return rc;42}43EXPORT_SYMBOL_GPL(bcm_phy_write_exp);44 45int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg)46{47 int val;48 49 val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);50 if (val < 0)51 return val;52 53 val = __phy_read(phydev, MII_BCM54XX_EXP_DATA);54 55 /* Restore default value. It's O.K. if this write fails. */56 __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);57 58 return val;59}60EXPORT_SYMBOL_GPL(__bcm_phy_read_exp);61 62int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)63{64 int rc;65 66 phy_lock_mdio_bus(phydev);67 rc = __bcm_phy_read_exp(phydev, reg);68 phy_unlock_mdio_bus(phydev);69 70 return rc;71}72EXPORT_SYMBOL_GPL(bcm_phy_read_exp);73 74int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)75{76 int new, ret;77 78 ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);79 if (ret < 0)80 return ret;81 82 ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA);83 if (ret < 0)84 return ret;85 86 new = (ret & ~mask) | set;87 if (new == ret)88 return 0;89 90 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new);91}92EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp);93 94int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)95{96 int ret;97 98 phy_lock_mdio_bus(phydev);99 ret = __bcm_phy_modify_exp(phydev, reg, mask, set);100 phy_unlock_mdio_bus(phydev);101 102 return ret;103}104EXPORT_SYMBOL_GPL(bcm_phy_modify_exp);105 106int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)107{108 /* The register must be written to both the Shadow Register Select and109 * the Shadow Read Register Selector110 */111 phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |112 regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);113 return phy_read(phydev, MII_BCM54XX_AUX_CTL);114}115EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);116 117int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)118{119 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);120}121EXPORT_SYMBOL(bcm54xx_auxctl_write);122 123int bcm_phy_write_misc(struct phy_device *phydev,124 u16 reg, u16 chl, u16 val)125{126 int rc;127 int tmp;128 129 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,130 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);131 if (rc < 0)132 return rc;133 134 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);135 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;136 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);137 if (rc < 0)138 return rc;139 140 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;141 rc = bcm_phy_write_exp(phydev, tmp, val);142 143 return rc;144}145EXPORT_SYMBOL_GPL(bcm_phy_write_misc);146 147int bcm_phy_read_misc(struct phy_device *phydev,148 u16 reg, u16 chl)149{150 int rc;151 int tmp;152 153 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,154 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);155 if (rc < 0)156 return rc;157 158 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);159 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;160 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);161 if (rc < 0)162 return rc;163 164 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;165 rc = bcm_phy_read_exp(phydev, tmp);166 167 return rc;168}169EXPORT_SYMBOL_GPL(bcm_phy_read_misc);170 171int bcm_phy_ack_intr(struct phy_device *phydev)172{173 int reg;174 175 /* Clear pending interrupts. */176 reg = phy_read(phydev, MII_BCM54XX_ISR);177 if (reg < 0)178 return reg;179 180 return 0;181}182EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);183 184int bcm_phy_config_intr(struct phy_device *phydev)185{186 int reg, err;187 188 reg = phy_read(phydev, MII_BCM54XX_ECR);189 if (reg < 0)190 return reg;191 192 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {193 err = bcm_phy_ack_intr(phydev);194 if (err)195 return err;196 197 reg &= ~MII_BCM54XX_ECR_IM;198 err = phy_write(phydev, MII_BCM54XX_ECR, reg);199 } else {200 reg |= MII_BCM54XX_ECR_IM;201 err = phy_write(phydev, MII_BCM54XX_ECR, reg);202 if (err)203 return err;204 205 err = bcm_phy_ack_intr(phydev);206 }207 return err;208}209EXPORT_SYMBOL_GPL(bcm_phy_config_intr);210 211irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev)212{213 int irq_status, irq_mask;214 215 irq_status = phy_read(phydev, MII_BCM54XX_ISR);216 if (irq_status < 0) {217 phy_error(phydev);218 return IRQ_NONE;219 }220 221 /* If a bit from the Interrupt Mask register is set, the corresponding222 * bit from the Interrupt Status register is masked. So read the IMR223 * and then flip the bits to get the list of possible interrupt224 * sources.225 */226 irq_mask = phy_read(phydev, MII_BCM54XX_IMR);227 if (irq_mask < 0) {228 phy_error(phydev);229 return IRQ_NONE;230 }231 irq_mask = ~irq_mask;232 233 if (!(irq_status & irq_mask))234 return IRQ_NONE;235 236 phy_trigger_machine(phydev);237 238 return IRQ_HANDLED;239}240EXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt);241 242int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)243{244 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));245 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));246}247EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);248 249int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,250 u16 val)251{252 return phy_write(phydev, MII_BCM54XX_SHD,253 MII_BCM54XX_SHD_WRITE |254 MII_BCM54XX_SHD_VAL(shadow) |255 MII_BCM54XX_SHD_DATA(val));256}257EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);258 259int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)260{261 int val;262 263 val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);264 if (val < 0)265 return val;266 267 return __phy_read(phydev, MII_BCM54XX_RDB_DATA);268}269EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);270 271int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)272{273 int ret;274 275 phy_lock_mdio_bus(phydev);276 ret = __bcm_phy_read_rdb(phydev, rdb);277 phy_unlock_mdio_bus(phydev);278 279 return ret;280}281EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);282 283int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)284{285 int ret;286 287 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);288 if (ret < 0)289 return ret;290 291 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);292}293EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);294 295int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)296{297 int ret;298 299 phy_lock_mdio_bus(phydev);300 ret = __bcm_phy_write_rdb(phydev, rdb, val);301 phy_unlock_mdio_bus(phydev);302 303 return ret;304}305EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);306 307int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)308{309 int new, ret;310 311 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);312 if (ret < 0)313 return ret;314 315 ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);316 if (ret < 0)317 return ret;318 319 new = (ret & ~mask) | set;320 if (new == ret)321 return 0;322 323 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);324}325EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);326 327int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)328{329 int ret;330 331 phy_lock_mdio_bus(phydev);332 ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);333 phy_unlock_mdio_bus(phydev);334 335 return ret;336}337EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);338 339int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)340{341 int val;342 343 if (dll_pwr_down) {344 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);345 if (val < 0)346 return val;347 348 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;349 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);350 }351 352 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);353 if (val < 0)354 return val;355 356 /* Clear APD bits */357 val &= BCM_APD_CLR_MASK;358 359 if (phydev->autoneg == AUTONEG_ENABLE)360 val |= BCM54XX_SHD_APD_EN;361 else362 val |= BCM_NO_ANEG_APD_EN;363 364 /* Enable energy detect single link pulse for easy wakeup */365 val |= BCM_APD_SINGLELP_EN;366 367 /* Enable Auto Power-Down (APD) for the PHY */368 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);369}370EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);371 372int bcm_phy_set_eee(struct phy_device *phydev, bool enable)373{374 int val, mask = 0;375 376 /* Enable EEE at PHY level */377 val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);378 if (val < 0)379 return val;380 381 if (enable)382 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;383 else384 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);385 386 phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);387 388 /* Advertise EEE */389 val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);390 if (val < 0)391 return val;392 393 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,394 phydev->supported))395 mask |= MDIO_EEE_1000T;396 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,397 phydev->supported))398 mask |= MDIO_EEE_100TX;399 400 if (enable)401 val |= mask;402 else403 val &= ~mask;404 405 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);406 407 return 0;408}409EXPORT_SYMBOL_GPL(bcm_phy_set_eee);410 411int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)412{413 int val;414 415 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);416 if (val < 0)417 return val;418 419 /* Check if wirespeed is enabled or not */420 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {421 *count = DOWNSHIFT_DEV_DISABLE;422 return 0;423 }424 425 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);426 if (val < 0)427 return val;428 429 /* Downgrade after one link attempt */430 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {431 *count = 1;432 } else {433 /* Downgrade after configured retry count */434 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;435 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;436 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;437 }438 439 return 0;440}441EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);442 443int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)444{445 int val = 0, ret = 0;446 447 /* Range check the number given */448 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >449 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&450 count != DOWNSHIFT_DEV_DEFAULT_COUNT) {451 return -ERANGE;452 }453 454 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);455 if (val < 0)456 return val;457 458 /* Se the write enable bit */459 val |= MII_BCM54XX_AUXCTL_MISC_WREN;460 461 if (count == DOWNSHIFT_DEV_DISABLE) {462 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;463 return bcm54xx_auxctl_write(phydev,464 MII_BCM54XX_AUXCTL_SHDWSEL_MISC,465 val);466 } else {467 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;468 ret = bcm54xx_auxctl_write(phydev,469 MII_BCM54XX_AUXCTL_SHDWSEL_MISC,470 val);471 if (ret < 0)472 return ret;473 }474 475 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);476 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<477 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |478 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);479 480 switch (count) {481 case 1:482 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;483 break;484 case DOWNSHIFT_DEV_DEFAULT_COUNT:485 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;486 break;487 default:488 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<489 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;490 break;491 }492 493 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);494}495EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);496 497struct bcm_phy_hw_stat {498 const char *string;499 int devad;500 u16 reg;501 u8 shift;502 u8 bits;503};504 505/* Counters freeze at either 0xffff or 0xff, better than nothing */506static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {507 { "phy_receive_errors", -1, MII_BRCM_CORE_BASE12, 0, 16 },508 { "phy_serdes_ber_errors", -1, MII_BRCM_CORE_BASE13, 8, 8 },509 { "phy_false_carrier_sense_errors", -1, MII_BRCM_CORE_BASE13, 0, 8 },510 { "phy_local_rcvr_nok", -1, MII_BRCM_CORE_BASE14, 8, 8 },511 { "phy_remote_rcv_nok", -1, MII_BRCM_CORE_BASE14, 0, 8 },512 { "phy_lpi_count", MDIO_MMD_AN, BRCM_CL45VEN_EEE_LPI_CNT, 0, 16 },513};514 515int bcm_phy_get_sset_count(struct phy_device *phydev)516{517 return ARRAY_SIZE(bcm_phy_hw_stats);518}519EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);520 521void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)522{523 unsigned int i;524 525 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)526 strscpy(data + i * ETH_GSTRING_LEN,527 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);528}529EXPORT_SYMBOL_GPL(bcm_phy_get_strings);530 531/* Caller is supposed to provide appropriate storage for the library code to532 * access the shadow copy533 */534static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,535 unsigned int i)536{537 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];538 int val;539 u64 ret;540 541 if (stat.devad < 0)542 val = phy_read(phydev, stat.reg);543 else544 val = phy_read_mmd(phydev, stat.devad, stat.reg);545 if (val < 0) {546 ret = U64_MAX;547 } else {548 val >>= stat.shift;549 val = val & ((1 << stat.bits) - 1);550 shadow[i] += val;551 ret = shadow[i];552 }553 554 return ret;555}556 557void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,558 struct ethtool_stats *stats, u64 *data)559{560 unsigned int i;561 562 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)563 data[i] = bcm_phy_get_stat(phydev, shadow, i);564}565EXPORT_SYMBOL_GPL(bcm_phy_get_stats);566 567void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)568{569 /* Reset R_CAL/RC_CAL Engine */570 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);571 572 /* Disable Reset R_AL/RC_CAL Engine */573 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);574}575EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);576 577int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)578{579 /* Increase VCO range to prevent unlocking problem of PLL at low580 * temp581 */582 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);583 584 /* Change Ki to 011 */585 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);586 587 /* Disable loading of TVCO buffer to bandgap, set bandgap trim588 * to 111589 */590 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);591 592 /* Adjust bias current trim by -3 */593 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);594 595 /* Switch to CORE_BASE1E */596 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);597 598 bcm_phy_r_rc_cal_reset(phydev);599 600 /* write AFE_RXCONFIG_0 */601 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);602 603 /* write AFE_RXCONFIG_1 */604 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);605 606 /* write AFE_RX_LP_COUNTER */607 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);608 609 /* write AFE_HPF_TRIM_OTHERS */610 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);611 612 /* write AFTE_TX_CONFIG */613 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);614 615 return 0;616}617EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);618 619int bcm_phy_enable_jumbo(struct phy_device *phydev)620{621 int ret;622 623 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);624 if (ret < 0)625 return ret;626 627 /* Enable extended length packet reception */628 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,629 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);630 if (ret < 0)631 return ret;632 633 /* Enable the elastic FIFO for raising the transmission limit from634 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation635 * latency.636 */637 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);638}639EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);640 641static int __bcm_phy_enable_rdb_access(struct phy_device *phydev)642{643 return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0);644}645 646static int __bcm_phy_enable_legacy_access(struct phy_device *phydev)647{648 return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087,649 BCM54XX_ACCESS_MODE_LEGACY_EN);650}651 652static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb)653{654 u16 mask, set;655 int ret;656 657 /* Auto-negotiation must be enabled for cable diagnostics to work, but658 * don't advertise any capabilities.659 */660 phy_write(phydev, MII_BMCR, BMCR_ANENABLE);661 phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);662 phy_write(phydev, MII_CTRL1000, 0);663 664 phy_lock_mdio_bus(phydev);665 if (is_rdb) {666 ret = __bcm_phy_enable_legacy_access(phydev);667 if (ret)668 goto out;669 }670 671 mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK;672 set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK |673 FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK,674 BCM54XX_ECD_CTRL_UNIT_CM);675 676 ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set);677 678out:679 /* re-enable the RDB access even if there was an error */680 if (is_rdb)681 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;682 683 phy_unlock_mdio_bus(phydev);684 685 return ret;686}687 688static int bcm_phy_cable_test_report_trans(int result)689{690 switch (result) {691 case BCM54XX_ECD_FAULT_TYPE_OK:692 return ETHTOOL_A_CABLE_RESULT_CODE_OK;693 case BCM54XX_ECD_FAULT_TYPE_OPEN:694 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;695 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:696 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;697 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:698 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;699 case BCM54XX_ECD_FAULT_TYPE_INVALID:700 case BCM54XX_ECD_FAULT_TYPE_BUSY:701 default:702 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;703 }704}705 706static bool bcm_phy_distance_valid(int result)707{708 switch (result) {709 case BCM54XX_ECD_FAULT_TYPE_OPEN:710 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:711 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:712 return true;713 }714 return false;715}716 717static int bcm_phy_report_length(struct phy_device *phydev, int pair)718{719 int val;720 721 val = __bcm_phy_read_exp(phydev,722 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair);723 if (val < 0)724 return val;725 726 if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID)727 return 0;728 729 ethnl_cable_test_fault_length(phydev, pair, val);730 731 return 0;732}733 734static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,735 bool *finished, bool is_rdb)736{737 int pair_a, pair_b, pair_c, pair_d, ret;738 739 *finished = false;740 741 phy_lock_mdio_bus(phydev);742 743 if (is_rdb) {744 ret = __bcm_phy_enable_legacy_access(phydev);745 if (ret)746 goto out;747 }748 749 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL);750 if (ret < 0)751 goto out;752 753 if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) {754 ret = 0;755 goto out;756 }757 758 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE);759 if (ret < 0)760 goto out;761 762 pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret);763 pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret);764 pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret);765 pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret);766 767 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,768 bcm_phy_cable_test_report_trans(pair_a));769 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,770 bcm_phy_cable_test_report_trans(pair_b));771 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,772 bcm_phy_cable_test_report_trans(pair_c));773 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,774 bcm_phy_cable_test_report_trans(pair_d));775 776 if (bcm_phy_distance_valid(pair_a))777 bcm_phy_report_length(phydev, 0);778 if (bcm_phy_distance_valid(pair_b))779 bcm_phy_report_length(phydev, 1);780 if (bcm_phy_distance_valid(pair_c))781 bcm_phy_report_length(phydev, 2);782 if (bcm_phy_distance_valid(pair_d))783 bcm_phy_report_length(phydev, 3);784 785 ret = 0;786 *finished = true;787out:788 /* re-enable the RDB access even if there was an error */789 if (is_rdb)790 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;791 792 phy_unlock_mdio_bus(phydev);793 794 return ret;795}796 797static int bcm_setup_lre_forced(struct phy_device *phydev)798{799 u16 ctl = 0;800 801 phydev->pause = 0;802 phydev->asym_pause = 0;803 804 if (phydev->speed == SPEED_100)805 ctl |= LRECR_SPEED100;806 807 if (phydev->duplex != DUPLEX_FULL)808 return -EOPNOTSUPP;809 810 return phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_SPEED100, ctl);811}812 813/**814 * bcm_linkmode_adv_to_lre_adv_t - translate linkmode advertisement to LDS815 * @advertising: the linkmode advertisement settings816 * Return: LDS Auto-Negotiation Advertised Ability register value817 *818 * A small helper function that translates linkmode advertisement819 * settings to phy LDS autonegotiation advertisements for the820 * MII_BCM54XX_LREANAA register of Broadcom PHYs capable of LDS821 */822static u32 bcm_linkmode_adv_to_lre_adv_t(unsigned long *advertising)823{824 u32 result = 0;825 826 if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,827 advertising))828 result |= LREANAA_10_1PAIR;829 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,830 advertising))831 result |= LREANAA_100_1PAIR;832 if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising))833 result |= LRELPA_PAUSE;834 if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising))835 result |= LRELPA_PAUSE_ASYM;836 837 return result;838}839 840int bcm_phy_cable_test_start(struct phy_device *phydev)841{842 return _bcm_phy_cable_test_start(phydev, false);843}844EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start);845 846int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished)847{848 return _bcm_phy_cable_test_get_status(phydev, finished, false);849}850EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status);851 852/* We assume that all PHYs which support RDB access can be switched to legacy853 * mode. If, in the future, this is not true anymore, we have to re-implement854 * this with RDB access.855 */856int bcm_phy_cable_test_start_rdb(struct phy_device *phydev)857{858 return _bcm_phy_cable_test_start(phydev, true);859}860EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb);861 862int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,863 bool *finished)864{865 return _bcm_phy_cable_test_get_status(phydev, finished, true);866}867EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);868 869#define BCM54XX_WOL_SUPPORTED_MASK (WAKE_UCAST | \870 WAKE_MCAST | \871 WAKE_BCAST | \872 WAKE_MAGIC | \873 WAKE_MAGICSECURE)874 875int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)876{877 struct net_device *ndev = phydev->attached_dev;878 u8 da[ETH_ALEN], mask[ETH_ALEN];879 unsigned int i;880 u16 ctl;881 int ret;882 883 /* Allow a MAC driver to play through its own Wake-on-LAN884 * implementation885 */886 if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK)887 return -EOPNOTSUPP;888 889 /* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's890 * ethtool only supports 6, for now.891 */892 BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN);893 894 /* Clear previous interrupts */895 ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);896 if (ret < 0)897 return ret;898 899 ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);900 if (ret < 0)901 return ret;902 903 ctl = ret;904 905 if (!wol->wolopts) {906 if (phy_interrupt_is_valid(phydev))907 disable_irq_wake(phydev->irq);908 909 /* Leave all interrupts disabled */910 ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK,911 BCM54XX_WOL_ALL_INTRS);912 if (ret < 0)913 return ret;914 915 /* Disable the global Wake-on-LAN enable bit */916 ctl &= ~BCM54XX_WOL_EN;917 918 return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);919 }920 921 /* Clear the previously configured mode and mask mode for Wake-on-LAN */922 ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT);923 ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT);924 ctl &= ~BCM54XX_WOL_DIR_PKT_EN;925 ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT);926 927 /* When using WAKE_MAGIC, we program the magic pattern filter to match928 * the device's MAC address and we accept any MAC DA in the Ethernet929 * frame.930 *931 * When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the932 * following:933 * - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match934 * - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care935 * - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match936 *937 * Note that the Broadcast MAC DA is inherently going to match the938 * multicast pattern being matched.939 */940 memset(mask, 0, sizeof(mask));941 942 if (wol->wolopts & WAKE_MCAST) {943 memset(da, 0, sizeof(da));944 memset(mask, 0xff, sizeof(mask));945 da[0] = 0x01;946 mask[0] = ~da[0];947 } else {948 if (wol->wolopts & WAKE_UCAST) {949 ether_addr_copy(da, ndev->dev_addr);950 } else if (wol->wolopts & WAKE_BCAST) {951 eth_broadcast_addr(da);952 } else if (wol->wolopts & WAKE_MAGICSECURE) {953 ether_addr_copy(da, wol->sopass);954 } else if (wol->wolopts & WAKE_MAGIC) {955 memset(da, 0, sizeof(da));956 memset(mask, 0xff, sizeof(mask));957 }958 }959 960 for (i = 0; i < ETH_ALEN / 2; i++) {961 if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {962 ret = bcm_phy_write_exp(phydev,963 BCM54XX_WOL_MPD_DATA1(2 - i),964 ndev->dev_addr[i * 2] << 8 |965 ndev->dev_addr[i * 2 + 1]);966 if (ret < 0)967 return ret;968 }969 970 ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i),971 da[i * 2] << 8 | da[i * 2 + 1]);972 if (ret < 0)973 return ret;974 975 ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i),976 mask[i * 2] << 8 | mask[i * 2 + 1]);977 if (ret)978 return ret;979 }980 981 if (wol->wolopts & WAKE_MAGICSECURE) {982 ctl |= BCM54XX_WOL_SECKEY_OPT_6B <<983 BCM54XX_WOL_SECKEY_OPT_SHIFT;984 ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT;985 ctl |= BCM54XX_WOL_MASK_MODE_DA_FF <<986 BCM54XX_WOL_MASK_MODE_SHIFT;987 } else {988 if (wol->wolopts & WAKE_MAGIC)989 ctl |= BCM54XX_WOL_MODE_SINGLE_MPD;990 else991 ctl |= BCM54XX_WOL_DIR_PKT_EN;992 ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY <<993 BCM54XX_WOL_MASK_MODE_SHIFT;994 }995 996 /* Globally enable Wake-on-LAN */997 ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK;998 999 ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);1000 if (ret < 0)1001 return ret;1002 1003 /* Enable WOL interrupt on LED4 */1004 ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL);1005 if (ret < 0)1006 return ret;1007 1008 ret |= BCM54XX_LED4_SEL_INTR;1009 ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret);1010 if (ret < 0)1011 return ret;1012 1013 /* Enable all Wake-on-LAN interrupt sources */1014 ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0);1015 if (ret < 0)1016 return ret;1017 1018 if (phy_interrupt_is_valid(phydev))1019 enable_irq_wake(phydev->irq);1020 1021 return 0;1022}1023EXPORT_SYMBOL_GPL(bcm_phy_set_wol);1024 1025void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)1026{1027 struct net_device *ndev = phydev->attached_dev;1028 u8 da[ETH_ALEN];1029 unsigned int i;1030 int ret;1031 u16 ctl;1032 1033 wol->supported = BCM54XX_WOL_SUPPORTED_MASK;1034 wol->wolopts = 0;1035 1036 ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);1037 if (ret < 0)1038 return;1039 1040 ctl = ret;1041 1042 if (!(ctl & BCM54XX_WOL_EN))1043 return;1044 1045 for (i = 0; i < sizeof(da) / 2; i++) {1046 ret = bcm_phy_read_exp(phydev,1047 BCM54XX_WOL_MPD_DATA2(2 - i));1048 if (ret < 0)1049 return;1050 1051 da[i * 2] = ret >> 8;1052 da[i * 2 + 1] = ret & 0xff;1053 }1054 1055 if (ctl & BCM54XX_WOL_DIR_PKT_EN) {1056 if (is_broadcast_ether_addr(da))1057 wol->wolopts |= WAKE_BCAST;1058 else if (is_multicast_ether_addr(da))1059 wol->wolopts |= WAKE_MCAST;1060 else if (ether_addr_equal(da, ndev->dev_addr))1061 wol->wolopts |= WAKE_UCAST;1062 } else {1063 ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK;1064 switch (ctl) {1065 case BCM54XX_WOL_MODE_SINGLE_MPD:1066 wol->wolopts |= WAKE_MAGIC;1067 break;1068 case BCM54XX_WOL_MODE_SINGLE_MPDSEC:1069 wol->wolopts |= WAKE_MAGICSECURE;1070 memcpy(wol->sopass, da, sizeof(da));1071 break;1072 default:1073 break;1074 }1075 }1076}1077EXPORT_SYMBOL_GPL(bcm_phy_get_wol);1078 1079irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id)1080{1081 return IRQ_HANDLED;1082}1083EXPORT_SYMBOL_GPL(bcm_phy_wol_isr);1084 1085int bcm_phy_led_brightness_set(struct phy_device *phydev,1086 u8 index, enum led_brightness value)1087{1088 u8 led_num;1089 int ret;1090 u16 reg;1091 1092 if (index >= 4)1093 return -EINVAL;1094 1095 /* Two LEDS per register */1096 led_num = index % 2;1097 reg = index >= 2 ? BCM54XX_SHD_LEDS2 : BCM54XX_SHD_LEDS1;1098 1099 ret = bcm_phy_read_shadow(phydev, reg);1100 if (ret < 0)1101 return ret;1102 1103 ret &= ~(BCM_LED_SRC_MASK << BCM54XX_SHD_LEDS_SHIFT(led_num));1104 if (value == LED_OFF)1105 ret |= BCM_LED_SRC_OFF << BCM54XX_SHD_LEDS_SHIFT(led_num);1106 else1107 ret |= BCM_LED_SRC_ON << BCM54XX_SHD_LEDS_SHIFT(led_num);1108 return bcm_phy_write_shadow(phydev, reg, ret);1109}1110EXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set);1111 1112int bcm_setup_lre_master_slave(struct phy_device *phydev)1113{1114 u16 ctl = 0;1115 1116 switch (phydev->master_slave_set) {1117 case MASTER_SLAVE_CFG_MASTER_PREFERRED:1118 case MASTER_SLAVE_CFG_MASTER_FORCE:1119 ctl = LRECR_MASTER;1120 break;1121 case MASTER_SLAVE_CFG_SLAVE_PREFERRED:1122 case MASTER_SLAVE_CFG_SLAVE_FORCE:1123 break;1124 case MASTER_SLAVE_CFG_UNKNOWN:1125 case MASTER_SLAVE_CFG_UNSUPPORTED:1126 return 0;1127 default:1128 phydev_warn(phydev, "Unsupported Master/Slave mode\n");1129 return -EOPNOTSUPP;1130 }1131 1132 return phy_modify_changed(phydev, MII_BCM54XX_LRECR, LRECR_MASTER, ctl);1133}1134EXPORT_SYMBOL_GPL(bcm_setup_lre_master_slave);1135 1136int bcm_config_lre_aneg(struct phy_device *phydev, bool changed)1137{1138 int err;1139 1140 if (genphy_config_eee_advert(phydev))1141 changed = true;1142 1143 err = bcm_setup_lre_master_slave(phydev);1144 if (err < 0)1145 return err;1146 else if (err)1147 changed = true;1148 1149 if (phydev->autoneg != AUTONEG_ENABLE)1150 return bcm_setup_lre_forced(phydev);1151 1152 err = bcm_config_lre_advert(phydev);1153 if (err < 0)1154 return err;1155 else if (err)1156 changed = true;1157 1158 return genphy_check_and_restart_aneg(phydev, changed);1159}1160EXPORT_SYMBOL_GPL(bcm_config_lre_aneg);1161 1162/**1163 * bcm_config_lre_advert - sanitize and advertise Long-Distance Signaling1164 * auto-negotiation parameters1165 * @phydev: target phy_device struct1166 * Return: 0 if the PHY's advertisement hasn't changed, < 0 on error,1167 * > 0 if it has changed1168 *1169 * Writes MII_BCM54XX_LREANAA with the appropriate values. The values are to be1170 * sanitized before, to make sure we only advertise what is supported.1171 * The sanitization is done already in phy_ethtool_ksettings_set()1172 */1173int bcm_config_lre_advert(struct phy_device *phydev)1174{1175 u32 adv = bcm_linkmode_adv_to_lre_adv_t(phydev->advertising);1176 1177 /* Setup BroadR-Reach mode advertisement */1178 return phy_modify_changed(phydev, MII_BCM54XX_LREANAA,1179 LRE_ADVERTISE_ALL | LREANAA_PAUSE |1180 LREANAA_PAUSE_ASYM, adv);1181}1182EXPORT_SYMBOL_GPL(bcm_config_lre_advert);1183 1184MODULE_DESCRIPTION("Broadcom PHY Library");1185MODULE_LICENSE("GPL v2");1186MODULE_AUTHOR("Broadcom Corporation");1187