brintos

brintos / linux-shallow public Read only

0
0
Text · 26.3 KiB · 97638ba Raw
963 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Broadcom BCM7xxx internal transceivers support.4 *5 * Copyright (C) 2014-2017 Broadcom6 */7 8#include <linux/module.h>9#include <linux/phy.h>10#include <linux/delay.h>11#include "bcm-phy-lib.h"12#include <linux/bitops.h>13#include <linux/brcmphy.h>14#include <linux/clk.h>15#include <linux/mdio.h>16 17/* Broadcom BCM7xxx internal PHY registers */18 19/* EPHY only register definitions */20#define MII_BCM7XXX_100TX_AUX_CTL	0x1021#define MII_BCM7XXX_100TX_FALSE_CAR	0x1322#define MII_BCM7XXX_100TX_DISC		0x1423#define MII_BCM7XXX_AUX_MODE		0x1d24#define  MII_BCM7XXX_64CLK_MDIO		BIT(12)25#define MII_BCM7XXX_TEST		0x1f26#define  MII_BCM7XXX_SHD_MODE_2		BIT(2)27#define MII_BCM7XXX_SHD_2_ADDR_CTRL	0xe28#define MII_BCM7XXX_SHD_2_CTRL_STAT	0xf29#define MII_BCM7XXX_SHD_2_BIAS_TRIM	0x1a30#define MII_BCM7XXX_SHD_3_PCS_CTRL	0x031#define MII_BCM7XXX_SHD_3_PCS_STATUS	0x132#define MII_BCM7XXX_SHD_3_EEE_CAP	0x233#define MII_BCM7XXX_SHD_3_AN_EEE_ADV	0x334#define MII_BCM7XXX_SHD_3_EEE_LP	0x435#define MII_BCM7XXX_SHD_3_EEE_WK_ERR	0x536#define MII_BCM7XXX_SHD_3_PCS_CTRL_2	0x637#define  MII_BCM7XXX_PCS_CTRL_2_DEF	0x440038#define MII_BCM7XXX_SHD_3_AN_STAT	0xb39#define  MII_BCM7XXX_AN_NULL_MSG_EN	BIT(0)40#define  MII_BCM7XXX_AN_EEE_EN		BIT(1)41#define MII_BCM7XXX_SHD_3_EEE_THRESH	0xe42#define  MII_BCM7XXX_EEE_THRESH_DEF	0x5043#define MII_BCM7XXX_SHD_3_TL4		0x2344#define  MII_BCM7XXX_TL4_RST_MSK	(BIT(2) | BIT(1))45 46struct bcm7xxx_phy_priv {47	u64	*stats;48};49 50static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)51{52	/* AFE_RXCONFIG_0 */53	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);54 55	/* AFE_RXCONFIG_1 */56	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);57 58	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */59	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);60 61	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */62	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);63 64	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */65	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);66 67	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */68	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);69 70	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */71	bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);72 73	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal74	 * offset for HT=0 code75	 */76	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);77 78	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */79	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);80 81	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */82	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);83 84	/* Reset R_CAL/RC_CAL engine */85	bcm_phy_r_rc_cal_reset(phydev);86 87	return 0;88}89 90static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)91{92	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */93	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);94 95	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */96	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);97 98	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */99	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);100 101	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal102	 * offset for HT=0 code103	 */104	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);105 106	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */107	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);108 109	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */110	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);111 112	/* Reset R_CAL/RC_CAL engine */113	bcm_phy_r_rc_cal_reset(phydev);114 115	return 0;116}117 118static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)119{120	/* +1 RC_CAL codes for RL centering for both LT and HT conditions */121	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);122 123	/* Cut master bias current by 2% to compensate for RC_CAL offset */124	bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);125 126	/* Improve hybrid leakage */127	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);128 129	/* Change rx_on_tune 8 to 0xf */130	bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);131 132	/* Change 100Tx EEE bandwidth */133	bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);134 135	/* Enable ffe zero detection for Vitesse interoperability */136	bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);137 138	bcm_phy_r_rc_cal_reset(phydev);139 140	return 0;141}142 143static int bcm7xxx_28nm_config_init(struct phy_device *phydev)144{145	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);146	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);147	u8 count;148	int ret = 0;149 150	/* Newer devices have moved the revision information back into a151	 * standard location in MII_PHYS_ID[23]152	 */153	if (rev == 0)154		rev = phydev->phy_id & ~phydev->drv->phy_id_mask;155 156	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",157		     phydev_name(phydev), phydev->drv->name, rev, patch);158 159	/* Dummy read to a register to workaround an issue upon reset where the160	 * internal inverter may not allow the first MDIO transaction to pass161	 * the MDIO management controller and make us return 0xffff for such162	 * reads.163	 */164	phy_read(phydev, MII_BMSR);165 166	switch (rev) {167	case 0xa0:168	case 0xb0:169		ret = bcm_phy_28nm_a0b0_afe_config_init(phydev);170		break;171	case 0xd0:172		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);173		break;174	case 0xe0:175	case 0xf0:176	/* Rev G0 introduces a roll over */177	case 0x10:178		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);179		break;180	case 0x01:181		ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);182		break;183	default:184		break;185	}186 187	if (ret)188		return ret;189 190	ret =  bcm_phy_enable_jumbo(phydev);191	if (ret)192		return ret;193 194	ret = bcm_phy_downshift_get(phydev, &count);195	if (ret)196		return ret;197 198	/* Only enable EEE if Wirespeed/downshift is disabled */199	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);200	if (ret)201		return ret;202 203	return bcm_phy_enable_apd(phydev, true);204}205 206static int bcm7xxx_28nm_resume(struct phy_device *phydev)207{208	int ret;209 210	/* Re-apply workarounds coming out suspend/resume */211	ret = bcm7xxx_28nm_config_init(phydev);212	if (ret)213		return ret;214 215	/* 28nm Gigabit PHYs come out of reset without any half-duplex216	 * or "hub" compliant advertised mode, fix that. This does not217	 * cause any problems with the PHY library since genphy_config_aneg()218	 * gracefully handles auto-negotiated and forced modes.219	 */220	return genphy_config_aneg(phydev);221}222 223static int __phy_set_clr_bits(struct phy_device *dev, int location,224			      int set_mask, int clr_mask)225{226	int v, ret;227 228	v = __phy_read(dev, location);229	if (v < 0)230		return v;231 232	v &= ~clr_mask;233	v |= set_mask;234 235	ret = __phy_write(dev, location, v);236	if (ret < 0)237		return ret;238 239	return v;240}241 242static int phy_set_clr_bits(struct phy_device *dev, int location,243			    int set_mask, int clr_mask)244{245	int ret;246 247	mutex_lock(&dev->mdio.bus->mdio_lock);248	ret = __phy_set_clr_bits(dev, location, set_mask, clr_mask);249	mutex_unlock(&dev->mdio.bus->mdio_lock);250 251	return ret;252}253 254static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev)255{256	int ret;257 258	/* set shadow mode 2 */259	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,260			       MII_BCM7XXX_SHD_MODE_2, 0);261	if (ret < 0)262		return ret;263 264	/* Set current trim values INT_trim = -1, Ext_trim =0 */265	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, 0x3BE0);266	if (ret < 0)267		goto reset_shadow_mode;268 269	/* Cal reset */270	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,271			MII_BCM7XXX_SHD_3_TL4);272	if (ret < 0)273		goto reset_shadow_mode;274	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,275			       MII_BCM7XXX_TL4_RST_MSK, 0);276	if (ret < 0)277		goto reset_shadow_mode;278 279	/* Cal reset disable */280	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,281			MII_BCM7XXX_SHD_3_TL4);282	if (ret < 0)283		goto reset_shadow_mode;284	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,285			       0, MII_BCM7XXX_TL4_RST_MSK);286	if (ret < 0)287		goto reset_shadow_mode;288 289reset_shadow_mode:290	/* reset shadow mode 2 */291	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,292			       MII_BCM7XXX_SHD_MODE_2);293	if (ret < 0)294		return ret;295 296	return 0;297}298 299/* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */300static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev)301{302	int ret;303 304	/* set shadow mode 1 */305	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST,306			       MII_BRCM_FET_BT_SRE, 0);307	if (ret < 0)308		return ret;309 310	/* Enable auto-power down */311	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,312			       MII_BRCM_FET_SHDW_AS2_APDE, 0);313	if (ret < 0)314		return ret;315 316	/* reset shadow mode 1 */317	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, 0,318			       MII_BRCM_FET_BT_SRE);319	if (ret < 0)320		return ret;321 322	return 0;323}324 325static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev)326{327	int ret;328 329	/* set shadow mode 2 */330	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,331			       MII_BCM7XXX_SHD_MODE_2, 0);332	if (ret < 0)333		return ret;334 335	/* Advertise supported modes */336	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,337			MII_BCM7XXX_SHD_3_AN_EEE_ADV);338	if (ret < 0)339		goto reset_shadow_mode;340	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,341			MDIO_EEE_100TX);342	if (ret < 0)343		goto reset_shadow_mode;344 345	/* Restore Defaults */346	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,347			MII_BCM7XXX_SHD_3_PCS_CTRL_2);348	if (ret < 0)349		goto reset_shadow_mode;350	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,351			MII_BCM7XXX_PCS_CTRL_2_DEF);352	if (ret < 0)353		goto reset_shadow_mode;354 355	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,356			MII_BCM7XXX_SHD_3_EEE_THRESH);357	if (ret < 0)358		goto reset_shadow_mode;359	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,360			MII_BCM7XXX_EEE_THRESH_DEF);361	if (ret < 0)362		goto reset_shadow_mode;363 364	/* Enable EEE autonegotiation */365	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,366			MII_BCM7XXX_SHD_3_AN_STAT);367	if (ret < 0)368		goto reset_shadow_mode;369	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,370			(MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN));371	if (ret < 0)372		goto reset_shadow_mode;373 374reset_shadow_mode:375	/* reset shadow mode 2 */376	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,377			       MII_BCM7XXX_SHD_MODE_2);378	if (ret < 0)379		return ret;380 381	/* Restart autoneg */382	phy_write(phydev, MII_BMCR,383		  (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART));384 385	return 0;386}387 388static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev)389{390	u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask;391	int ret = 0;392 393	pr_info_once("%s: %s PHY revision: 0x%02x\n",394		     phydev_name(phydev), phydev->drv->name, rev);395 396	/* Dummy read to a register to workaround a possible issue upon reset397	 * where the internal inverter may not allow the first MDIO transaction398	 * to pass the MDIO management controller and make us return 0xffff for399	 * such reads.400	 */401	phy_read(phydev, MII_BMSR);402 403	/* Apply AFE software work-around if necessary */404	if (rev == 0x01) {405		ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev);406		if (ret)407			return ret;408	}409 410	ret = bcm7xxx_28nm_ephy_eee_enable(phydev);411	if (ret)412		return ret;413 414	return bcm7xxx_28nm_ephy_apd_enable(phydev);415}416 417static int bcm7xxx_16nm_ephy_afe_config(struct phy_device *phydev)418{419	int tmp, rcalcode, rcalnewcodelp, rcalnewcode11, rcalnewcode11d2;420 421	/* Reset PHY */422	tmp = genphy_soft_reset(phydev);423	if (tmp)424		return tmp;425 426	/* Reset AFE and PLL */427	bcm_phy_write_exp_sel(phydev, 0x0003, 0x0006);428	/* Clear reset */429	bcm_phy_write_exp_sel(phydev, 0x0003, 0x0000);430 431	/* Write PLL/AFE control register to select 54MHz crystal */432	bcm_phy_write_misc(phydev, 0x0030, 0x0001, 0x0000);433	bcm_phy_write_misc(phydev, 0x0031, 0x0000, 0x044a);434 435	/* Change Ka,Kp,Ki to pdiv=1 */436	bcm_phy_write_misc(phydev, 0x0033, 0x0002, 0x71a1);437	/* Configuration override */438	bcm_phy_write_misc(phydev, 0x0033, 0x0001, 0x8000);439 440	/* Change PLL_NDIV and PLL_NUDGE */441	bcm_phy_write_misc(phydev, 0x0031, 0x0001, 0x2f68);442	bcm_phy_write_misc(phydev, 0x0031, 0x0002, 0x0000);443 444	/* Reference frequency is 54Mhz, config_mode[15:14] = 3 (low445	 * phase)446	 */447	bcm_phy_write_misc(phydev, 0x0030, 0x0003, 0xc036);448 449	/* Initialize bypass mode */450	bcm_phy_write_misc(phydev, 0x0032, 0x0003, 0x0000);451	/* Bypass code, default: VCOCLK enabled */452	bcm_phy_write_misc(phydev, 0x0033, 0x0000, 0x0002);453	/* LDOs at default setting */454	bcm_phy_write_misc(phydev, 0x0030, 0x0002, 0x01c0);455	/* Release PLL reset */456	bcm_phy_write_misc(phydev, 0x0030, 0x0001, 0x0001);457 458	/* Bandgap curvature correction to correct default */459	bcm_phy_write_misc(phydev, 0x0038, 0x0000, 0x0010);460 461	/* Run RCAL */462	bcm_phy_write_misc(phydev, 0x0039, 0x0003, 0x0038);463	bcm_phy_write_misc(phydev, 0x0039, 0x0003, 0x003b);464	udelay(2);465	bcm_phy_write_misc(phydev, 0x0039, 0x0003, 0x003f);466	mdelay(5);467 468	/* AFE_CAL_CONFIG_0, Vref=1000, Target=10, averaging enabled */469	bcm_phy_write_misc(phydev, 0x0039, 0x0001, 0x1c82);470	/* AFE_CAL_CONFIG_0, no reset and analog powerup */471	bcm_phy_write_misc(phydev, 0x0039, 0x0001, 0x9e82);472	udelay(2);473	/* AFE_CAL_CONFIG_0, start calibration */474	bcm_phy_write_misc(phydev, 0x0039, 0x0001, 0x9f82);475	udelay(100);476	/* AFE_CAL_CONFIG_0, clear start calibration, set HiBW */477	bcm_phy_write_misc(phydev, 0x0039, 0x0001, 0x9e86);478	udelay(2);479	/* AFE_CAL_CONFIG_0, start calibration with hi BW mode set */480	bcm_phy_write_misc(phydev, 0x0039, 0x0001, 0x9f86);481	udelay(100);482 483	/* Adjust 10BT amplitude additional +7% and 100BT +2% */484	bcm_phy_write_misc(phydev, 0x0038, 0x0001, 0xe7ea);485	/* Adjust 1G mode amplitude and 1G testmode1 */486	bcm_phy_write_misc(phydev, 0x0038, 0x0002, 0xede0);487 488	/* Read CORE_EXPA9 */489	tmp = bcm_phy_read_exp_sel(phydev, 0x00a9);490	/* CORE_EXPA9[6:1] is rcalcode[5:0] */491	rcalcode = (tmp & 0x7e) / 2;492	/* Correct RCAL code + 1 is -1% rprogr, LP: +16 */493	rcalnewcodelp = rcalcode + 16;494	/* Correct RCAL code + 1 is -15 rprogr, 11: +10 */495	rcalnewcode11 = rcalcode + 10;496	/* Saturate if necessary */497	if (rcalnewcodelp > 0x3f)498		rcalnewcodelp = 0x3f;499	if (rcalnewcode11 > 0x3f)500		rcalnewcode11 = 0x3f;501	/* REXT=1 BYP=1 RCAL_st1<5:0>=new rcal code */502	tmp = 0x00f8 + rcalnewcodelp * 256;503	/* Program into AFE_CAL_CONFIG_2 */504	bcm_phy_write_misc(phydev, 0x0039, 0x0003, tmp);505	/* AFE_BIAS_CONFIG_0 10BT bias code (Bias: E4) */506	bcm_phy_write_misc(phydev, 0x0038, 0x0001, 0xe7e4);507	/* invert adc clock output and 'adc refp ldo current To correct508	 * default509	 */510	bcm_phy_write_misc(phydev, 0x003b, 0x0000, 0x8002);511	/* 100BT stair case, high BW, 1G stair case, alternate encode */512	bcm_phy_write_misc(phydev, 0x003c, 0x0003, 0xf882);513	/* 1000BT DAC transition method per Erol, bits[32], DAC Shuffle514	 * sequence 1 + 10BT imp adjust bits515	 */516	bcm_phy_write_misc(phydev, 0x003d, 0x0000, 0x3201);517	/* Non-overlap fix */518	bcm_phy_write_misc(phydev, 0x003a, 0x0002, 0x0c00);519 520	/* pwdb override (rxconfig<5>) to turn on RX LDO indpendent of521	 * pwdb controls from DSP_TAP10522	 */523	bcm_phy_write_misc(phydev, 0x003a, 0x0001, 0x0020);524 525	/* Remove references to channel 2 and 3 */526	bcm_phy_write_misc(phydev, 0x003b, 0x0002, 0x0000);527	bcm_phy_write_misc(phydev, 0x003b, 0x0003, 0x0000);528 529	/* Set cal_bypassb bit rxconfig<43> */530	bcm_phy_write_misc(phydev, 0x003a, 0x0003, 0x0800);531	udelay(2);532 533	/* Revert pwdb_override (rxconfig<5>) to 0 so that the RX pwr534	 * is controlled by DSP.535	 */536	bcm_phy_write_misc(phydev, 0x003a, 0x0001, 0x0000);537 538	/* Drop LSB */539	rcalnewcode11d2 = (rcalnewcode11 & 0xfffe) / 2;540	tmp = bcm_phy_read_misc(phydev, 0x003d, 0x0001);541	/* Clear bits [11:5] */542	tmp &= ~0xfe0;543	/* set txcfg_ch0<5>=1 (enable + set local rcal) */544	tmp |= 0x0020 | (rcalnewcode11d2 * 64);545	bcm_phy_write_misc(phydev, 0x003d, 0x0001, tmp);546	bcm_phy_write_misc(phydev, 0x003d, 0x0002, tmp);547 548	tmp = bcm_phy_read_misc(phydev, 0x003d, 0x0000);549	/* set txcfg<45:44>=11 (enable Rextra + invert fullscaledetect)550	 */551	tmp &= ~0x3000;552	tmp |= 0x3000;553	bcm_phy_write_misc(phydev, 0x003d, 0x0000, tmp);554 555	return 0;556}557 558static int bcm7xxx_16nm_ephy_config_init(struct phy_device *phydev)559{560	int ret, val;561 562	ret = bcm7xxx_16nm_ephy_afe_config(phydev);563	if (ret)564		return ret;565 566	ret = bcm_phy_set_eee(phydev, true);567	if (ret)568		return ret;569 570	ret = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);571	if (ret < 0)572		return ret;573 574	val = ret;575 576	/* Auto power down of DLL enabled,577	 * TXC/RXC disabled during auto power down.578	 */579	val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;580	val |= BIT(8);581 582	ret = bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);583	if (ret < 0)584		return ret;585 586	return bcm_phy_enable_apd(phydev, true);587}588 589static int bcm7xxx_16nm_ephy_resume(struct phy_device *phydev)590{591	int ret;592 593	/* Re-apply workarounds coming out suspend/resume */594	ret = bcm7xxx_16nm_ephy_config_init(phydev);595	if (ret)596		return ret;597 598	return genphy_config_aneg(phydev);599}600 601#define MII_BCM7XXX_REG_INVALID	0xff602 603static u8 bcm7xxx_28nm_ephy_regnum_to_shd(u16 regnum)604{605	switch (regnum) {606	case MDIO_CTRL1:607		return MII_BCM7XXX_SHD_3_PCS_CTRL;608	case MDIO_STAT1:609		return MII_BCM7XXX_SHD_3_PCS_STATUS;610	case MDIO_PCS_EEE_ABLE:611		return MII_BCM7XXX_SHD_3_EEE_CAP;612	case MDIO_AN_EEE_ADV:613		return MII_BCM7XXX_SHD_3_AN_EEE_ADV;614	case MDIO_AN_EEE_LPABLE:615		return MII_BCM7XXX_SHD_3_EEE_LP;616	case MDIO_PCS_EEE_WK_ERR:617		return MII_BCM7XXX_SHD_3_EEE_WK_ERR;618	default:619		return MII_BCM7XXX_REG_INVALID;620	}621}622 623static bool bcm7xxx_28nm_ephy_dev_valid(int devnum)624{625	return devnum == MDIO_MMD_AN || devnum == MDIO_MMD_PCS;626}627 628static int bcm7xxx_28nm_ephy_read_mmd(struct phy_device *phydev,629				      int devnum, u16 regnum)630{631	u8 shd = bcm7xxx_28nm_ephy_regnum_to_shd(regnum);632	int ret;633 634	if (!bcm7xxx_28nm_ephy_dev_valid(devnum) ||635	    shd == MII_BCM7XXX_REG_INVALID)636		return -EOPNOTSUPP;637 638	/* set shadow mode 2 */639	ret = __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,640				 MII_BCM7XXX_SHD_MODE_2, 0);641	if (ret < 0)642		return ret;643 644	/* Access the desired shadow register address */645	ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd);646	if (ret < 0)647		goto reset_shadow_mode;648 649	ret = __phy_read(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT);650 651reset_shadow_mode:652	/* reset shadow mode 2 */653	__phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,654			   MII_BCM7XXX_SHD_MODE_2);655	return ret;656}657 658static int bcm7xxx_28nm_ephy_write_mmd(struct phy_device *phydev,659				       int devnum, u16 regnum, u16 val)660{661	u8 shd = bcm7xxx_28nm_ephy_regnum_to_shd(regnum);662	int ret;663 664	if (!bcm7xxx_28nm_ephy_dev_valid(devnum) ||665	    shd == MII_BCM7XXX_REG_INVALID)666		return -EOPNOTSUPP;667 668	/* set shadow mode 2 */669	ret = __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,670				 MII_BCM7XXX_SHD_MODE_2, 0);671	if (ret < 0)672		return ret;673 674	/* Access the desired shadow register address */675	ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, shd);676	if (ret < 0)677		goto reset_shadow_mode;678 679	/* Write the desired value in the shadow register */680	__phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, val);681 682reset_shadow_mode:683	/* reset shadow mode 2 */684	return __phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,685				  MII_BCM7XXX_SHD_MODE_2);686}687 688static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev)689{690	int ret;691 692	/* Re-apply workarounds coming out suspend/resume */693	ret = bcm7xxx_28nm_ephy_config_init(phydev);694	if (ret)695		return ret;696 697	return genphy_config_aneg(phydev);698}699 700static int bcm7xxx_config_init(struct phy_device *phydev)701{702	int ret;703 704	/* Enable 64 clock MDIO */705	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);706	phy_read(phydev, MII_BCM7XXX_AUX_MODE);707 708	/* set shadow mode 2 */709	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,710			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);711	if (ret < 0)712		return ret;713 714	/* set iddq_clkbias */715	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);716	udelay(10);717 718	/* reset iddq_clkbias */719	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);720 721	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);722 723	/* reset shadow mode 2 */724	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);725	if (ret < 0)726		return ret;727 728	return 0;729}730 731/* Workaround for putting the PHY in IDDQ mode, required732 * for all BCM7XXX 40nm and 65nm PHYs733 */734static int bcm7xxx_suspend(struct phy_device *phydev)735{736	int ret;737	static const struct bcm7xxx_regs {738		int reg;739		u16 value;740	} bcm7xxx_suspend_cfg[] = {741		{ MII_BCM7XXX_TEST, 0x008b },742		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },743		{ MII_BCM7XXX_100TX_DISC, 0x7000 },744		{ MII_BCM7XXX_TEST, 0x000f },745		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },746		{ MII_BCM7XXX_TEST, 0x000b },747	};748	unsigned int i;749 750	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {751		ret = phy_write(phydev,752				bcm7xxx_suspend_cfg[i].reg,753				bcm7xxx_suspend_cfg[i].value);754		if (ret)755			return ret;756	}757 758	return 0;759}760 761static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,762				    struct ethtool_tunable *tuna,763				    void *data)764{765	switch (tuna->id) {766	case ETHTOOL_PHY_DOWNSHIFT:767		return bcm_phy_downshift_get(phydev, (u8 *)data);768	default:769		return -EOPNOTSUPP;770	}771}772 773static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,774				    struct ethtool_tunable *tuna,775				    const void *data)776{777	u8 count = *(u8 *)data;778	int ret;779 780	switch (tuna->id) {781	case ETHTOOL_PHY_DOWNSHIFT:782		ret = bcm_phy_downshift_set(phydev, count);783		break;784	default:785		return -EOPNOTSUPP;786	}787 788	if (ret)789		return ret;790 791	/* Disable EEE advertisement since this prevents the PHY792	 * from successfully linking up, trigger auto-negotiation restart793	 * to let the MAC decide what to do.794	 */795	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);796	if (ret)797		return ret;798 799	return genphy_restart_aneg(phydev);800}801 802static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,803				       struct ethtool_stats *stats, u64 *data)804{805	struct bcm7xxx_phy_priv *priv = phydev->priv;806 807	bcm_phy_get_stats(phydev, priv->stats, stats, data);808}809 810static int bcm7xxx_28nm_probe(struct phy_device *phydev)811{812	struct bcm7xxx_phy_priv *priv;813	struct clk *clk;814	int ret = 0;815 816	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);817	if (!priv)818		return -ENOMEM;819 820	phydev->priv = priv;821 822	priv->stats = devm_kcalloc(&phydev->mdio.dev,823				   bcm_phy_get_sset_count(phydev), sizeof(u64),824				   GFP_KERNEL);825	if (!priv->stats)826		return -ENOMEM;827 828	clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, NULL);829	if (IS_ERR(clk))830		return PTR_ERR(clk);831 832	/* Dummy read to a register to workaround an issue upon reset where the833	 * internal inverter may not allow the first MDIO transaction to pass834	 * the MDIO management controller and make us return 0xffff for such835	 * reads. This is needed to ensure that any subsequent reads to the836	 * PHY will succeed.837	 */838	phy_read(phydev, MII_BMSR);839 840	return ret;841}842 843#define BCM7XXX_28NM_GPHY(_oui, _name)					\844{									\845	.phy_id		= (_oui),					\846	.phy_id_mask	= 0xfffffff0,					\847	.name		= _name,					\848	/* PHY_GBIT_FEATURES */						\849	.flags		= PHY_IS_INTERNAL,				\850	.config_init	= bcm7xxx_28nm_config_init,			\851	.resume		= bcm7xxx_28nm_resume,				\852	.get_tunable	= bcm7xxx_28nm_get_tunable,			\853	.set_tunable	= bcm7xxx_28nm_set_tunable,			\854	.get_sset_count	= bcm_phy_get_sset_count,			\855	.get_strings	= bcm_phy_get_strings,				\856	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\857	.probe		= bcm7xxx_28nm_probe,				\858}859 860#define BCM7XXX_28NM_EPHY(_oui, _name)					\861{									\862	.phy_id		= (_oui),					\863	.phy_id_mask	= 0xfffffff0,					\864	.name		= _name,					\865	/* PHY_BASIC_FEATURES */					\866	.flags		= PHY_IS_INTERNAL,				\867	.config_init	= bcm7xxx_28nm_ephy_config_init,		\868	.resume		= bcm7xxx_28nm_ephy_resume,			\869	.get_sset_count	= bcm_phy_get_sset_count,			\870	.get_strings	= bcm_phy_get_strings,				\871	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\872	.probe		= bcm7xxx_28nm_probe,				\873	.read_mmd	= bcm7xxx_28nm_ephy_read_mmd,			\874	.write_mmd	= bcm7xxx_28nm_ephy_write_mmd,			\875}876 877#define BCM7XXX_40NM_EPHY(_oui, _name)					\878{									\879	.phy_id         = (_oui),					\880	.phy_id_mask    = 0xfffffff0,					\881	.name           = _name,					\882	/* PHY_BASIC_FEATURES */					\883	.flags          = PHY_IS_INTERNAL,				\884	.soft_reset	= genphy_soft_reset,				\885	.config_init    = bcm7xxx_config_init,				\886	.suspend        = bcm7xxx_suspend,				\887	.resume         = bcm7xxx_config_init,				\888}889 890#define BCM7XXX_16NM_EPHY(_oui, _name)					\891{									\892	.phy_id		= (_oui),					\893	.phy_id_mask	= 0xfffffff0,					\894	.name		= _name,					\895	/* PHY_BASIC_FEATURES */					\896	.flags		= PHY_IS_INTERNAL,				\897	.get_sset_count	= bcm_phy_get_sset_count,			\898	.get_strings	= bcm_phy_get_strings,				\899	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\900	.probe		= bcm7xxx_28nm_probe,				\901	.config_init	= bcm7xxx_16nm_ephy_config_init,		\902	.config_aneg	= genphy_config_aneg,				\903	.read_status	= genphy_read_status,				\904	.resume		= bcm7xxx_16nm_ephy_resume,			\905}906 907static struct phy_driver bcm7xxx_driver[] = {908	BCM7XXX_28NM_EPHY(PHY_ID_BCM72113, "Broadcom BCM72113"),909	BCM7XXX_28NM_EPHY(PHY_ID_BCM72116, "Broadcom BCM72116"),910	BCM7XXX_16NM_EPHY(PHY_ID_BCM72165, "Broadcom BCM72165"),911	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),912	BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"),913	BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"),914	BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"),915	BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"),916	BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),917	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),918	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),919	BCM7XXX_16NM_EPHY(PHY_ID_BCM74165, "Broadcom BCM74165"),920	BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"),921	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),922	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),923	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),924	BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),925	BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),926	BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),927	BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),928	BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),929	BCM7XXX_16NM_EPHY(PHY_ID_BCM7712, "Broadcom BCM7712"),930};931 932static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {933	{ PHY_ID_BCM72113, 0xfffffff0 },934	{ PHY_ID_BCM72116, 0xfffffff0, },935	{ PHY_ID_BCM72165, 0xfffffff0, },936	{ PHY_ID_BCM7250, 0xfffffff0, },937	{ PHY_ID_BCM7255, 0xfffffff0, },938	{ PHY_ID_BCM7260, 0xfffffff0, },939	{ PHY_ID_BCM7268, 0xfffffff0, },940	{ PHY_ID_BCM7271, 0xfffffff0, },941	{ PHY_ID_BCM7278, 0xfffffff0, },942	{ PHY_ID_BCM7364, 0xfffffff0, },943	{ PHY_ID_BCM7366, 0xfffffff0, },944	{ PHY_ID_BCM7346, 0xfffffff0, },945	{ PHY_ID_BCM7362, 0xfffffff0, },946	{ PHY_ID_BCM7425, 0xfffffff0, },947	{ PHY_ID_BCM7429, 0xfffffff0, },948	{ PHY_ID_BCM74371, 0xfffffff0, },949	{ PHY_ID_BCM7439, 0xfffffff0, },950	{ PHY_ID_BCM7435, 0xfffffff0, },951	{ PHY_ID_BCM7445, 0xfffffff0, },952	{ PHY_ID_BCM7712, 0xfffffff0, },953	{ }954};955 956module_phy_driver(bcm7xxx_driver);957 958MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);959 960MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");961MODULE_LICENSE("GPL");962MODULE_AUTHOR("Broadcom Corporation");963