Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2015-2017 Broadcom
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     /* Restore default value.  It's O.K. if this write fails. */
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     /* The register must be written to both the Shadow Register Select and
0107      * the Shadow Read Register Selector
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     /* Clear pending interrupts.  */
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     /* If a bit from the Interrupt Mask register is set, the corresponding
0220      * bit from the Interrupt Status register is masked. So read the IMR
0221      * and then flip the bits to get the list of possible interrupt
0222      * sources.
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     /* Clear APD bits */
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     /* Enable energy detect single link pulse for easy wakeup */
0363     val |= BCM_APD_SINGLELP_EN;
0364 
0365     /* Enable Auto Power-Down (APD) for the PHY */
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     /* Enable EEE at PHY level */
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     /* Advertise EEE */
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     /* Check if wirespeed is enabled or not */
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     /* Downgrade after one link attempt */
0428     if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
0429         *count = 1;
0430     } else {
0431         /* Downgrade after configured retry count */
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     /* Range check the number given */
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     /* Se the write enable bit */
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 /* Counters freeze at either 0xffff or 0xff, better than nothing */
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 /* Caller is supposed to provide appropriate storage for the library code to
0528  * access the shadow copy
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     /* Reset R_CAL/RC_CAL Engine */
0563     bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
0564 
0565     /* Disable Reset R_AL/RC_CAL Engine */
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     /* Increase VCO range to prevent unlocking problem of PLL at low
0573      * temp
0574      */
0575     bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
0576 
0577     /* Change Ki to 011 */
0578     bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
0579 
0580     /* Disable loading of TVCO buffer to bandgap, set bandgap trim
0581      * to 111
0582      */
0583     bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
0584 
0585     /* Adjust bias current trim by -3 */
0586     bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
0587 
0588     /* Switch to CORE_BASE1E */
0589     phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
0590 
0591     bcm_phy_r_rc_cal_reset(phydev);
0592 
0593     /* write AFE_RXCONFIG_0 */
0594     bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
0595 
0596     /* write AFE_RXCONFIG_1 */
0597     bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
0598 
0599     /* write AFE_RX_LP_COUNTER */
0600     bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
0601 
0602     /* write AFE_HPF_TRIM_OTHERS */
0603     bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
0604 
0605     /* write AFTE_TX_CONFIG */
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     /* Enable extended length packet reception */
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     /* Enable the elastic FIFO for raising the transmission limit from
0627      * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation
0628      * latency.
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     /* Auto-negotiation must be enabled for cable diagnostics to work, but
0651      * don't advertise any capabilities.
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     /* re-enable the RDB access even if there was an error */
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     /* re-enable the RDB access even if there was an error */
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 /* We assume that all PHYs which support RDB access can be switched to legacy
0803  * mode. If, in the future, this is not true anymore, we have to re-implement
0804  * this with RDB access.
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");