cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bcm-phy-lib.c (20005B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2015-2017 Broadcom
      4 */
      5
      6#include "bcm-phy-lib.h"
      7#include <linux/bitfield.h>
      8#include <linux/brcmphy.h>
      9#include <linux/export.h>
     10#include <linux/mdio.h>
     11#include <linux/module.h>
     12#include <linux/phy.h>
     13#include <linux/ethtool.h>
     14#include <linux/ethtool_netlink.h>
     15
     16#define MII_BCM_CHANNEL_WIDTH     0x2000
     17#define BCM_CL45VEN_EEE_ADV       0x3c
     18
     19int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
     20{
     21	int rc;
     22
     23	rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
     24	if (rc < 0)
     25		return rc;
     26
     27	return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
     28}
     29EXPORT_SYMBOL_GPL(__bcm_phy_write_exp);
     30
     31int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
     32{
     33	int rc;
     34
     35	phy_lock_mdio_bus(phydev);
     36	rc = __bcm_phy_write_exp(phydev, reg, val);
     37	phy_unlock_mdio_bus(phydev);
     38
     39	return rc;
     40}
     41EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
     42
     43int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
     44{
     45	int val;
     46
     47	val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
     48	if (val < 0)
     49		return val;
     50
     51	val = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
     52
     53	/* Restore default value.  It's O.K. if this write fails. */
     54	__phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
     55
     56	return val;
     57}
     58EXPORT_SYMBOL_GPL(__bcm_phy_read_exp);
     59
     60int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
     61{
     62	int rc;
     63
     64	phy_lock_mdio_bus(phydev);
     65	rc = __bcm_phy_read_exp(phydev, reg);
     66	phy_unlock_mdio_bus(phydev);
     67
     68	return rc;
     69}
     70EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
     71
     72int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
     73{
     74	int new, ret;
     75
     76	ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
     77	if (ret < 0)
     78		return ret;
     79
     80	ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
     81	if (ret < 0)
     82		return ret;
     83
     84	new = (ret & ~mask) | set;
     85	if (new == ret)
     86		return 0;
     87
     88	return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new);
     89}
     90EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp);
     91
     92int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
     93{
     94	int ret;
     95
     96	phy_lock_mdio_bus(phydev);
     97	ret = __bcm_phy_modify_exp(phydev, reg, mask, set);
     98	phy_unlock_mdio_bus(phydev);
     99
    100	return ret;
    101}
    102EXPORT_SYMBOL_GPL(bcm_phy_modify_exp);
    103
    104int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
    105{
    106	/* The register must be written to both the Shadow Register Select and
    107	 * the Shadow Read Register Selector
    108	 */
    109	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |
    110		  regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
    111	return phy_read(phydev, MII_BCM54XX_AUX_CTL);
    112}
    113EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
    114
    115int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
    116{
    117	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
    118}
    119EXPORT_SYMBOL(bcm54xx_auxctl_write);
    120
    121int bcm_phy_write_misc(struct phy_device *phydev,
    122		       u16 reg, u16 chl, u16 val)
    123{
    124	int rc;
    125	int tmp;
    126
    127	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
    128		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
    129	if (rc < 0)
    130		return rc;
    131
    132	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
    133	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
    134	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
    135	if (rc < 0)
    136		return rc;
    137
    138	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
    139	rc = bcm_phy_write_exp(phydev, tmp, val);
    140
    141	return rc;
    142}
    143EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
    144
    145int bcm_phy_read_misc(struct phy_device *phydev,
    146		      u16 reg, u16 chl)
    147{
    148	int rc;
    149	int tmp;
    150
    151	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
    152		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
    153	if (rc < 0)
    154		return rc;
    155
    156	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
    157	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
    158	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
    159	if (rc < 0)
    160		return rc;
    161
    162	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
    163	rc = bcm_phy_read_exp(phydev, tmp);
    164
    165	return rc;
    166}
    167EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
    168
    169int bcm_phy_ack_intr(struct phy_device *phydev)
    170{
    171	int reg;
    172
    173	/* Clear pending interrupts.  */
    174	reg = phy_read(phydev, MII_BCM54XX_ISR);
    175	if (reg < 0)
    176		return reg;
    177
    178	return 0;
    179}
    180EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
    181
    182int bcm_phy_config_intr(struct phy_device *phydev)
    183{
    184	int reg, err;
    185
    186	reg = phy_read(phydev, MII_BCM54XX_ECR);
    187	if (reg < 0)
    188		return reg;
    189
    190	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
    191		err = bcm_phy_ack_intr(phydev);
    192		if (err)
    193			return err;
    194
    195		reg &= ~MII_BCM54XX_ECR_IM;
    196		err = phy_write(phydev, MII_BCM54XX_ECR, reg);
    197	} else {
    198		reg |= MII_BCM54XX_ECR_IM;
    199		err = phy_write(phydev, MII_BCM54XX_ECR, reg);
    200		if (err)
    201			return err;
    202
    203		err = bcm_phy_ack_intr(phydev);
    204	}
    205	return err;
    206}
    207EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
    208
    209irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev)
    210{
    211	int irq_status, irq_mask;
    212
    213	irq_status = phy_read(phydev, MII_BCM54XX_ISR);
    214	if (irq_status < 0) {
    215		phy_error(phydev);
    216		return IRQ_NONE;
    217	}
    218
    219	/* If a bit from the Interrupt Mask register is set, the corresponding
    220	 * bit from the Interrupt Status register is masked. So read the IMR
    221	 * and then flip the bits to get the list of possible interrupt
    222	 * sources.
    223	 */
    224	irq_mask = phy_read(phydev, MII_BCM54XX_IMR);
    225	if (irq_mask < 0) {
    226		phy_error(phydev);
    227		return IRQ_NONE;
    228	}
    229	irq_mask = ~irq_mask;
    230
    231	if (!(irq_status & irq_mask))
    232		return IRQ_NONE;
    233
    234	phy_trigger_machine(phydev);
    235
    236	return IRQ_HANDLED;
    237}
    238EXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt);
    239
    240int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
    241{
    242	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
    243	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
    244}
    245EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
    246
    247int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
    248			 u16 val)
    249{
    250	return phy_write(phydev, MII_BCM54XX_SHD,
    251			 MII_BCM54XX_SHD_WRITE |
    252			 MII_BCM54XX_SHD_VAL(shadow) |
    253			 MII_BCM54XX_SHD_DATA(val));
    254}
    255EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
    256
    257int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
    258{
    259	int val;
    260
    261	val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
    262	if (val < 0)
    263		return val;
    264
    265	return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
    266}
    267EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
    268
    269int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
    270{
    271	int ret;
    272
    273	phy_lock_mdio_bus(phydev);
    274	ret = __bcm_phy_read_rdb(phydev, rdb);
    275	phy_unlock_mdio_bus(phydev);
    276
    277	return ret;
    278}
    279EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
    280
    281int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
    282{
    283	int ret;
    284
    285	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
    286	if (ret < 0)
    287		return ret;
    288
    289	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
    290}
    291EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
    292
    293int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
    294{
    295	int ret;
    296
    297	phy_lock_mdio_bus(phydev);
    298	ret = __bcm_phy_write_rdb(phydev, rdb, val);
    299	phy_unlock_mdio_bus(phydev);
    300
    301	return ret;
    302}
    303EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
    304
    305int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
    306{
    307	int new, ret;
    308
    309	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
    310	if (ret < 0)
    311		return ret;
    312
    313	ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
    314	if (ret < 0)
    315		return ret;
    316
    317	new = (ret & ~mask) | set;
    318	if (new == ret)
    319		return 0;
    320
    321	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
    322}
    323EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
    324
    325int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
    326{
    327	int ret;
    328
    329	phy_lock_mdio_bus(phydev);
    330	ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
    331	phy_unlock_mdio_bus(phydev);
    332
    333	return ret;
    334}
    335EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
    336
    337int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
    338{
    339	int val;
    340
    341	if (dll_pwr_down) {
    342		val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
    343		if (val < 0)
    344			return val;
    345
    346		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
    347		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
    348	}
    349
    350	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
    351	if (val < 0)
    352		return val;
    353
    354	/* Clear APD bits */
    355	val &= BCM_APD_CLR_MASK;
    356
    357	if (phydev->autoneg == AUTONEG_ENABLE)
    358		val |= BCM54XX_SHD_APD_EN;
    359	else
    360		val |= BCM_NO_ANEG_APD_EN;
    361
    362	/* Enable energy detect single link pulse for easy wakeup */
    363	val |= BCM_APD_SINGLELP_EN;
    364
    365	/* Enable Auto Power-Down (APD) for the PHY */
    366	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
    367}
    368EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
    369
    370int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
    371{
    372	int val, mask = 0;
    373
    374	/* Enable EEE at PHY level */
    375	val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);
    376	if (val < 0)
    377		return val;
    378
    379	if (enable)
    380		val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
    381	else
    382		val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
    383
    384	phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);
    385
    386	/* Advertise EEE */
    387	val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);
    388	if (val < 0)
    389		return val;
    390
    391	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
    392			      phydev->supported))
    393		mask |= MDIO_EEE_1000T;
    394	if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
    395			      phydev->supported))
    396		mask |= MDIO_EEE_100TX;
    397
    398	if (enable)
    399		val |= mask;
    400	else
    401		val &= ~mask;
    402
    403	phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);
    404
    405	return 0;
    406}
    407EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
    408
    409int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
    410{
    411	int val;
    412
    413	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
    414	if (val < 0)
    415		return val;
    416
    417	/* Check if wirespeed is enabled or not */
    418	if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
    419		*count = DOWNSHIFT_DEV_DISABLE;
    420		return 0;
    421	}
    422
    423	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
    424	if (val < 0)
    425		return val;
    426
    427	/* Downgrade after one link attempt */
    428	if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
    429		*count = 1;
    430	} else {
    431		/* Downgrade after configured retry count */
    432		val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
    433		val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
    434		*count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
    435	}
    436
    437	return 0;
    438}
    439EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
    440
    441int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
    442{
    443	int val = 0, ret = 0;
    444
    445	/* Range check the number given */
    446	if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
    447	    BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
    448	    count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
    449		return -ERANGE;
    450	}
    451
    452	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
    453	if (val < 0)
    454		return val;
    455
    456	/* Se the write enable bit */
    457	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
    458
    459	if (count == DOWNSHIFT_DEV_DISABLE) {
    460		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
    461		return bcm54xx_auxctl_write(phydev,
    462					    MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
    463					    val);
    464	} else {
    465		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
    466		ret = bcm54xx_auxctl_write(phydev,
    467					   MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
    468					   val);
    469		if (ret < 0)
    470			return ret;
    471	}
    472
    473	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
    474	val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
    475		 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
    476		 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
    477
    478	switch (count) {
    479	case 1:
    480		val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
    481		break;
    482	case DOWNSHIFT_DEV_DEFAULT_COUNT:
    483		val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
    484		break;
    485	default:
    486		val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
    487			BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
    488		break;
    489	}
    490
    491	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
    492}
    493EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
    494
    495struct bcm_phy_hw_stat {
    496	const char *string;
    497	u8 reg;
    498	u8 shift;
    499	u8 bits;
    500};
    501
    502/* Counters freeze at either 0xffff or 0xff, better than nothing */
    503static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
    504	{ "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
    505	{ "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
    506	{ "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
    507	{ "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
    508	{ "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
    509};
    510
    511int bcm_phy_get_sset_count(struct phy_device *phydev)
    512{
    513	return ARRAY_SIZE(bcm_phy_hw_stats);
    514}
    515EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
    516
    517void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
    518{
    519	unsigned int i;
    520
    521	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
    522		strlcpy(data + i * ETH_GSTRING_LEN,
    523			bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
    524}
    525EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
    526
    527/* Caller is supposed to provide appropriate storage for the library code to
    528 * access the shadow copy
    529 */
    530static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
    531			    unsigned int i)
    532{
    533	struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
    534	int val;
    535	u64 ret;
    536
    537	val = phy_read(phydev, stat.reg);
    538	if (val < 0) {
    539		ret = U64_MAX;
    540	} else {
    541		val >>= stat.shift;
    542		val = val & ((1 << stat.bits) - 1);
    543		shadow[i] += val;
    544		ret = shadow[i];
    545	}
    546
    547	return ret;
    548}
    549
    550void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
    551		       struct ethtool_stats *stats, u64 *data)
    552{
    553	unsigned int i;
    554
    555	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
    556		data[i] = bcm_phy_get_stat(phydev, shadow, i);
    557}
    558EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
    559
    560void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
    561{
    562	/* Reset R_CAL/RC_CAL Engine */
    563	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
    564
    565	/* Disable Reset R_AL/RC_CAL Engine */
    566	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
    567}
    568EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
    569
    570int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
    571{
    572	/* Increase VCO range to prevent unlocking problem of PLL at low
    573	 * temp
    574	 */
    575	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
    576
    577	/* Change Ki to 011 */
    578	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
    579
    580	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
    581	 * to 111
    582	 */
    583	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
    584
    585	/* Adjust bias current trim by -3 */
    586	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
    587
    588	/* Switch to CORE_BASE1E */
    589	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
    590
    591	bcm_phy_r_rc_cal_reset(phydev);
    592
    593	/* write AFE_RXCONFIG_0 */
    594	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
    595
    596	/* write AFE_RXCONFIG_1 */
    597	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
    598
    599	/* write AFE_RX_LP_COUNTER */
    600	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
    601
    602	/* write AFE_HPF_TRIM_OTHERS */
    603	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
    604
    605	/* write AFTE_TX_CONFIG */
    606	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
    607
    608	return 0;
    609}
    610EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
    611
    612int bcm_phy_enable_jumbo(struct phy_device *phydev)
    613{
    614	int ret;
    615
    616	ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);
    617	if (ret < 0)
    618		return ret;
    619
    620	/* Enable extended length packet reception */
    621	ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
    622				   ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);
    623	if (ret < 0)
    624		return ret;
    625
    626	/* Enable the elastic FIFO for raising the transmission limit from
    627	 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation
    628	 * latency.
    629	 */
    630	return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);
    631}
    632EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);
    633
    634static int __bcm_phy_enable_rdb_access(struct phy_device *phydev)
    635{
    636	return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0);
    637}
    638
    639static int __bcm_phy_enable_legacy_access(struct phy_device *phydev)
    640{
    641	return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087,
    642				   BCM54XX_ACCESS_MODE_LEGACY_EN);
    643}
    644
    645static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb)
    646{
    647	u16 mask, set;
    648	int ret;
    649
    650	/* Auto-negotiation must be enabled for cable diagnostics to work, but
    651	 * don't advertise any capabilities.
    652	 */
    653	phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
    654	phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
    655	phy_write(phydev, MII_CTRL1000, 0);
    656
    657	phy_lock_mdio_bus(phydev);
    658	if (is_rdb) {
    659		ret = __bcm_phy_enable_legacy_access(phydev);
    660		if (ret)
    661			goto out;
    662	}
    663
    664	mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK;
    665	set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK |
    666	      FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK,
    667			 BCM54XX_ECD_CTRL_UNIT_CM);
    668
    669	ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set);
    670
    671out:
    672	/* re-enable the RDB access even if there was an error */
    673	if (is_rdb)
    674		ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
    675
    676	phy_unlock_mdio_bus(phydev);
    677
    678	return ret;
    679}
    680
    681static int bcm_phy_cable_test_report_trans(int result)
    682{
    683	switch (result) {
    684	case BCM54XX_ECD_FAULT_TYPE_OK:
    685		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
    686	case BCM54XX_ECD_FAULT_TYPE_OPEN:
    687		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
    688	case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
    689		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
    690	case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
    691		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
    692	case BCM54XX_ECD_FAULT_TYPE_INVALID:
    693	case BCM54XX_ECD_FAULT_TYPE_BUSY:
    694	default:
    695		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
    696	}
    697}
    698
    699static bool bcm_phy_distance_valid(int result)
    700{
    701	switch (result) {
    702	case BCM54XX_ECD_FAULT_TYPE_OPEN:
    703	case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
    704	case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
    705		return true;
    706	}
    707	return false;
    708}
    709
    710static int bcm_phy_report_length(struct phy_device *phydev, int pair)
    711{
    712	int val;
    713
    714	val = __bcm_phy_read_exp(phydev,
    715				 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair);
    716	if (val < 0)
    717		return val;
    718
    719	if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID)
    720		return 0;
    721
    722	ethnl_cable_test_fault_length(phydev, pair, val);
    723
    724	return 0;
    725}
    726
    727static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
    728					  bool *finished, bool is_rdb)
    729{
    730	int pair_a, pair_b, pair_c, pair_d, ret;
    731
    732	*finished = false;
    733
    734	phy_lock_mdio_bus(phydev);
    735
    736	if (is_rdb) {
    737		ret = __bcm_phy_enable_legacy_access(phydev);
    738		if (ret)
    739			goto out;
    740	}
    741
    742	ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL);
    743	if (ret < 0)
    744		goto out;
    745
    746	if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) {
    747		ret = 0;
    748		goto out;
    749	}
    750
    751	ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE);
    752	if (ret < 0)
    753		goto out;
    754
    755	pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret);
    756	pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret);
    757	pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret);
    758	pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret);
    759
    760	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
    761				bcm_phy_cable_test_report_trans(pair_a));
    762	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
    763				bcm_phy_cable_test_report_trans(pair_b));
    764	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
    765				bcm_phy_cable_test_report_trans(pair_c));
    766	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
    767				bcm_phy_cable_test_report_trans(pair_d));
    768
    769	if (bcm_phy_distance_valid(pair_a))
    770		bcm_phy_report_length(phydev, 0);
    771	if (bcm_phy_distance_valid(pair_b))
    772		bcm_phy_report_length(phydev, 1);
    773	if (bcm_phy_distance_valid(pair_c))
    774		bcm_phy_report_length(phydev, 2);
    775	if (bcm_phy_distance_valid(pair_d))
    776		bcm_phy_report_length(phydev, 3);
    777
    778	ret = 0;
    779	*finished = true;
    780out:
    781	/* re-enable the RDB access even if there was an error */
    782	if (is_rdb)
    783		ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
    784
    785	phy_unlock_mdio_bus(phydev);
    786
    787	return ret;
    788}
    789
    790int bcm_phy_cable_test_start(struct phy_device *phydev)
    791{
    792	return _bcm_phy_cable_test_start(phydev, false);
    793}
    794EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start);
    795
    796int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished)
    797{
    798	return _bcm_phy_cable_test_get_status(phydev, finished, false);
    799}
    800EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status);
    801
    802/* We assume that all PHYs which support RDB access can be switched to legacy
    803 * mode. If, in the future, this is not true anymore, we have to re-implement
    804 * this with RDB access.
    805 */
    806int bcm_phy_cable_test_start_rdb(struct phy_device *phydev)
    807{
    808	return _bcm_phy_cable_test_start(phydev, true);
    809}
    810EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb);
    811
    812int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,
    813				      bool *finished)
    814{
    815	return _bcm_phy_cable_test_get_status(phydev, finished, true);
    816}
    817EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);
    818
    819MODULE_DESCRIPTION("Broadcom PHY Library");
    820MODULE_LICENSE("GPL v2");
    821MODULE_AUTHOR("Broadcom Corporation");