0001
0002
0003
0004
0005
0006 #include "bcm-phy-lib.h"
0007 #include <linux/bitfield.h>
0008 #include <linux/brcmphy.h>
0009 #include <linux/export.h>
0010 #include <linux/mdio.h>
0011 #include <linux/module.h>
0012 #include <linux/phy.h>
0013 #include <linux/ethtool.h>
0014 #include <linux/ethtool_netlink.h>
0015
0016 #define MII_BCM_CHANNEL_WIDTH 0x2000
0017 #define BCM_CL45VEN_EEE_ADV 0x3c
0018
0019 int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
0020 {
0021 int rc;
0022
0023 rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
0024 if (rc < 0)
0025 return rc;
0026
0027 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
0028 }
0029 EXPORT_SYMBOL_GPL(__bcm_phy_write_exp);
0030
0031 int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
0032 {
0033 int rc;
0034
0035 phy_lock_mdio_bus(phydev);
0036 rc = __bcm_phy_write_exp(phydev, reg, val);
0037 phy_unlock_mdio_bus(phydev);
0038
0039 return rc;
0040 }
0041 EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
0042
0043 int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
0044 {
0045 int val;
0046
0047 val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
0048 if (val < 0)
0049 return val;
0050
0051 val = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
0052
0053
0054 __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
0055
0056 return val;
0057 }
0058 EXPORT_SYMBOL_GPL(__bcm_phy_read_exp);
0059
0060 int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
0061 {
0062 int rc;
0063
0064 phy_lock_mdio_bus(phydev);
0065 rc = __bcm_phy_read_exp(phydev, reg);
0066 phy_unlock_mdio_bus(phydev);
0067
0068 return rc;
0069 }
0070 EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
0071
0072 int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
0073 {
0074 int new, ret;
0075
0076 ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
0077 if (ret < 0)
0078 return ret;
0079
0080 ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
0081 if (ret < 0)
0082 return ret;
0083
0084 new = (ret & ~mask) | set;
0085 if (new == ret)
0086 return 0;
0087
0088 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new);
0089 }
0090 EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp);
0091
0092 int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
0093 {
0094 int ret;
0095
0096 phy_lock_mdio_bus(phydev);
0097 ret = __bcm_phy_modify_exp(phydev, reg, mask, set);
0098 phy_unlock_mdio_bus(phydev);
0099
0100 return ret;
0101 }
0102 EXPORT_SYMBOL_GPL(bcm_phy_modify_exp);
0103
0104 int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
0105 {
0106
0107
0108
0109 phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |
0110 regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
0111 return phy_read(phydev, MII_BCM54XX_AUX_CTL);
0112 }
0113 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
0114
0115 int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
0116 {
0117 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
0118 }
0119 EXPORT_SYMBOL(bcm54xx_auxctl_write);
0120
0121 int bcm_phy_write_misc(struct phy_device *phydev,
0122 u16 reg, u16 chl, u16 val)
0123 {
0124 int rc;
0125 int tmp;
0126
0127 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
0128 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
0129 if (rc < 0)
0130 return rc;
0131
0132 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
0133 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
0134 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
0135 if (rc < 0)
0136 return rc;
0137
0138 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
0139 rc = bcm_phy_write_exp(phydev, tmp, val);
0140
0141 return rc;
0142 }
0143 EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
0144
0145 int bcm_phy_read_misc(struct phy_device *phydev,
0146 u16 reg, u16 chl)
0147 {
0148 int rc;
0149 int tmp;
0150
0151 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
0152 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
0153 if (rc < 0)
0154 return rc;
0155
0156 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
0157 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
0158 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
0159 if (rc < 0)
0160 return rc;
0161
0162 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
0163 rc = bcm_phy_read_exp(phydev, tmp);
0164
0165 return rc;
0166 }
0167 EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
0168
0169 int bcm_phy_ack_intr(struct phy_device *phydev)
0170 {
0171 int reg;
0172
0173
0174 reg = phy_read(phydev, MII_BCM54XX_ISR);
0175 if (reg < 0)
0176 return reg;
0177
0178 return 0;
0179 }
0180 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
0181
0182 int bcm_phy_config_intr(struct phy_device *phydev)
0183 {
0184 int reg, err;
0185
0186 reg = phy_read(phydev, MII_BCM54XX_ECR);
0187 if (reg < 0)
0188 return reg;
0189
0190 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0191 err = bcm_phy_ack_intr(phydev);
0192 if (err)
0193 return err;
0194
0195 reg &= ~MII_BCM54XX_ECR_IM;
0196 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
0197 } else {
0198 reg |= MII_BCM54XX_ECR_IM;
0199 err = phy_write(phydev, MII_BCM54XX_ECR, reg);
0200 if (err)
0201 return err;
0202
0203 err = bcm_phy_ack_intr(phydev);
0204 }
0205 return err;
0206 }
0207 EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
0208
0209 irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev)
0210 {
0211 int irq_status, irq_mask;
0212
0213 irq_status = phy_read(phydev, MII_BCM54XX_ISR);
0214 if (irq_status < 0) {
0215 phy_error(phydev);
0216 return IRQ_NONE;
0217 }
0218
0219
0220
0221
0222
0223
0224 irq_mask = phy_read(phydev, MII_BCM54XX_IMR);
0225 if (irq_mask < 0) {
0226 phy_error(phydev);
0227 return IRQ_NONE;
0228 }
0229 irq_mask = ~irq_mask;
0230
0231 if (!(irq_status & irq_mask))
0232 return IRQ_NONE;
0233
0234 phy_trigger_machine(phydev);
0235
0236 return IRQ_HANDLED;
0237 }
0238 EXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt);
0239
0240 int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
0241 {
0242 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
0243 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
0244 }
0245 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
0246
0247 int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
0248 u16 val)
0249 {
0250 return phy_write(phydev, MII_BCM54XX_SHD,
0251 MII_BCM54XX_SHD_WRITE |
0252 MII_BCM54XX_SHD_VAL(shadow) |
0253 MII_BCM54XX_SHD_DATA(val));
0254 }
0255 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
0256
0257 int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
0258 {
0259 int val;
0260
0261 val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
0262 if (val < 0)
0263 return val;
0264
0265 return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
0266 }
0267 EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
0268
0269 int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
0270 {
0271 int ret;
0272
0273 phy_lock_mdio_bus(phydev);
0274 ret = __bcm_phy_read_rdb(phydev, rdb);
0275 phy_unlock_mdio_bus(phydev);
0276
0277 return ret;
0278 }
0279 EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
0280
0281 int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
0282 {
0283 int ret;
0284
0285 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
0286 if (ret < 0)
0287 return ret;
0288
0289 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
0290 }
0291 EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
0292
0293 int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
0294 {
0295 int ret;
0296
0297 phy_lock_mdio_bus(phydev);
0298 ret = __bcm_phy_write_rdb(phydev, rdb, val);
0299 phy_unlock_mdio_bus(phydev);
0300
0301 return ret;
0302 }
0303 EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
0304
0305 int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
0306 {
0307 int new, ret;
0308
0309 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
0310 if (ret < 0)
0311 return ret;
0312
0313 ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
0314 if (ret < 0)
0315 return ret;
0316
0317 new = (ret & ~mask) | set;
0318 if (new == ret)
0319 return 0;
0320
0321 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
0322 }
0323 EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
0324
0325 int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
0326 {
0327 int ret;
0328
0329 phy_lock_mdio_bus(phydev);
0330 ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
0331 phy_unlock_mdio_bus(phydev);
0332
0333 return ret;
0334 }
0335 EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
0336
0337 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
0338 {
0339 int val;
0340
0341 if (dll_pwr_down) {
0342 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
0343 if (val < 0)
0344 return val;
0345
0346 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
0347 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
0348 }
0349
0350 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
0351 if (val < 0)
0352 return val;
0353
0354
0355 val &= BCM_APD_CLR_MASK;
0356
0357 if (phydev->autoneg == AUTONEG_ENABLE)
0358 val |= BCM54XX_SHD_APD_EN;
0359 else
0360 val |= BCM_NO_ANEG_APD_EN;
0361
0362
0363 val |= BCM_APD_SINGLELP_EN;
0364
0365
0366 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
0367 }
0368 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
0369
0370 int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
0371 {
0372 int val, mask = 0;
0373
0374
0375 val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);
0376 if (val < 0)
0377 return val;
0378
0379 if (enable)
0380 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
0381 else
0382 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
0383
0384 phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);
0385
0386
0387 val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);
0388 if (val < 0)
0389 return val;
0390
0391 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
0392 phydev->supported))
0393 mask |= MDIO_EEE_1000T;
0394 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
0395 phydev->supported))
0396 mask |= MDIO_EEE_100TX;
0397
0398 if (enable)
0399 val |= mask;
0400 else
0401 val &= ~mask;
0402
0403 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);
0404
0405 return 0;
0406 }
0407 EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
0408
0409 int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
0410 {
0411 int val;
0412
0413 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
0414 if (val < 0)
0415 return val;
0416
0417
0418 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
0419 *count = DOWNSHIFT_DEV_DISABLE;
0420 return 0;
0421 }
0422
0423 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
0424 if (val < 0)
0425 return val;
0426
0427
0428 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
0429 *count = 1;
0430 } else {
0431
0432 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
0433 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
0434 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
0435 }
0436
0437 return 0;
0438 }
0439 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
0440
0441 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
0442 {
0443 int val = 0, ret = 0;
0444
0445
0446 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
0447 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
0448 count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
0449 return -ERANGE;
0450 }
0451
0452 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
0453 if (val < 0)
0454 return val;
0455
0456
0457 val |= MII_BCM54XX_AUXCTL_MISC_WREN;
0458
0459 if (count == DOWNSHIFT_DEV_DISABLE) {
0460 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
0461 return bcm54xx_auxctl_write(phydev,
0462 MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
0463 val);
0464 } else {
0465 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
0466 ret = bcm54xx_auxctl_write(phydev,
0467 MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
0468 val);
0469 if (ret < 0)
0470 return ret;
0471 }
0472
0473 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
0474 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
0475 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
0476 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
0477
0478 switch (count) {
0479 case 1:
0480 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
0481 break;
0482 case DOWNSHIFT_DEV_DEFAULT_COUNT:
0483 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
0484 break;
0485 default:
0486 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
0487 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
0488 break;
0489 }
0490
0491 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
0492 }
0493 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
0494
0495 struct bcm_phy_hw_stat {
0496 const char *string;
0497 u8 reg;
0498 u8 shift;
0499 u8 bits;
0500 };
0501
0502
0503 static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
0504 { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
0505 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
0506 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
0507 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
0508 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
0509 };
0510
0511 int bcm_phy_get_sset_count(struct phy_device *phydev)
0512 {
0513 return ARRAY_SIZE(bcm_phy_hw_stats);
0514 }
0515 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
0516
0517 void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
0518 {
0519 unsigned int i;
0520
0521 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
0522 strlcpy(data + i * ETH_GSTRING_LEN,
0523 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
0524 }
0525 EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
0526
0527
0528
0529
0530 static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
0531 unsigned int i)
0532 {
0533 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
0534 int val;
0535 u64 ret;
0536
0537 val = phy_read(phydev, stat.reg);
0538 if (val < 0) {
0539 ret = U64_MAX;
0540 } else {
0541 val >>= stat.shift;
0542 val = val & ((1 << stat.bits) - 1);
0543 shadow[i] += val;
0544 ret = shadow[i];
0545 }
0546
0547 return ret;
0548 }
0549
0550 void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
0551 struct ethtool_stats *stats, u64 *data)
0552 {
0553 unsigned int i;
0554
0555 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
0556 data[i] = bcm_phy_get_stat(phydev, shadow, i);
0557 }
0558 EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
0559
0560 void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
0561 {
0562
0563 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
0564
0565
0566 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
0567 }
0568 EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
0569
0570 int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
0571 {
0572
0573
0574
0575 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
0576
0577
0578 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
0579
0580
0581
0582
0583 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
0584
0585
0586 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
0587
0588
0589 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
0590
0591 bcm_phy_r_rc_cal_reset(phydev);
0592
0593
0594 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
0595
0596
0597 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
0598
0599
0600 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
0601
0602
0603 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
0604
0605
0606 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
0607
0608 return 0;
0609 }
0610 EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
0611
0612 int bcm_phy_enable_jumbo(struct phy_device *phydev)
0613 {
0614 int ret;
0615
0616 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);
0617 if (ret < 0)
0618 return ret;
0619
0620
0621 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
0622 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);
0623 if (ret < 0)
0624 return ret;
0625
0626
0627
0628
0629
0630 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);
0631 }
0632 EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);
0633
0634 static int __bcm_phy_enable_rdb_access(struct phy_device *phydev)
0635 {
0636 return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0);
0637 }
0638
0639 static int __bcm_phy_enable_legacy_access(struct phy_device *phydev)
0640 {
0641 return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087,
0642 BCM54XX_ACCESS_MODE_LEGACY_EN);
0643 }
0644
0645 static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb)
0646 {
0647 u16 mask, set;
0648 int ret;
0649
0650
0651
0652
0653 phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
0654 phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
0655 phy_write(phydev, MII_CTRL1000, 0);
0656
0657 phy_lock_mdio_bus(phydev);
0658 if (is_rdb) {
0659 ret = __bcm_phy_enable_legacy_access(phydev);
0660 if (ret)
0661 goto out;
0662 }
0663
0664 mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK;
0665 set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK |
0666 FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK,
0667 BCM54XX_ECD_CTRL_UNIT_CM);
0668
0669 ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set);
0670
0671 out:
0672
0673 if (is_rdb)
0674 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
0675
0676 phy_unlock_mdio_bus(phydev);
0677
0678 return ret;
0679 }
0680
0681 static int bcm_phy_cable_test_report_trans(int result)
0682 {
0683 switch (result) {
0684 case BCM54XX_ECD_FAULT_TYPE_OK:
0685 return ETHTOOL_A_CABLE_RESULT_CODE_OK;
0686 case BCM54XX_ECD_FAULT_TYPE_OPEN:
0687 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
0688 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
0689 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
0690 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
0691 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
0692 case BCM54XX_ECD_FAULT_TYPE_INVALID:
0693 case BCM54XX_ECD_FAULT_TYPE_BUSY:
0694 default:
0695 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
0696 }
0697 }
0698
0699 static bool bcm_phy_distance_valid(int result)
0700 {
0701 switch (result) {
0702 case BCM54XX_ECD_FAULT_TYPE_OPEN:
0703 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
0704 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
0705 return true;
0706 }
0707 return false;
0708 }
0709
0710 static int bcm_phy_report_length(struct phy_device *phydev, int pair)
0711 {
0712 int val;
0713
0714 val = __bcm_phy_read_exp(phydev,
0715 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair);
0716 if (val < 0)
0717 return val;
0718
0719 if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID)
0720 return 0;
0721
0722 ethnl_cable_test_fault_length(phydev, pair, val);
0723
0724 return 0;
0725 }
0726
0727 static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
0728 bool *finished, bool is_rdb)
0729 {
0730 int pair_a, pair_b, pair_c, pair_d, ret;
0731
0732 *finished = false;
0733
0734 phy_lock_mdio_bus(phydev);
0735
0736 if (is_rdb) {
0737 ret = __bcm_phy_enable_legacy_access(phydev);
0738 if (ret)
0739 goto out;
0740 }
0741
0742 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL);
0743 if (ret < 0)
0744 goto out;
0745
0746 if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) {
0747 ret = 0;
0748 goto out;
0749 }
0750
0751 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE);
0752 if (ret < 0)
0753 goto out;
0754
0755 pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret);
0756 pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret);
0757 pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret);
0758 pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret);
0759
0760 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
0761 bcm_phy_cable_test_report_trans(pair_a));
0762 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
0763 bcm_phy_cable_test_report_trans(pair_b));
0764 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
0765 bcm_phy_cable_test_report_trans(pair_c));
0766 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
0767 bcm_phy_cable_test_report_trans(pair_d));
0768
0769 if (bcm_phy_distance_valid(pair_a))
0770 bcm_phy_report_length(phydev, 0);
0771 if (bcm_phy_distance_valid(pair_b))
0772 bcm_phy_report_length(phydev, 1);
0773 if (bcm_phy_distance_valid(pair_c))
0774 bcm_phy_report_length(phydev, 2);
0775 if (bcm_phy_distance_valid(pair_d))
0776 bcm_phy_report_length(phydev, 3);
0777
0778 ret = 0;
0779 *finished = true;
0780 out:
0781
0782 if (is_rdb)
0783 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
0784
0785 phy_unlock_mdio_bus(phydev);
0786
0787 return ret;
0788 }
0789
0790 int bcm_phy_cable_test_start(struct phy_device *phydev)
0791 {
0792 return _bcm_phy_cable_test_start(phydev, false);
0793 }
0794 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start);
0795
0796 int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished)
0797 {
0798 return _bcm_phy_cable_test_get_status(phydev, finished, false);
0799 }
0800 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status);
0801
0802
0803
0804
0805
0806 int bcm_phy_cable_test_start_rdb(struct phy_device *phydev)
0807 {
0808 return _bcm_phy_cable_test_start(phydev, true);
0809 }
0810 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb);
0811
0812 int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,
0813 bool *finished)
0814 {
0815 return _bcm_phy_cable_test_get_status(phydev, finished, true);
0816 }
0817 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);
0818
0819 MODULE_DESCRIPTION("Broadcom PHY Library");
0820 MODULE_LICENSE("GPL v2");
0821 MODULE_AUTHOR("Broadcom Corporation");