xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision cc1122b0)
1b560a58cSFlorian Fainelli /*
2b560a58cSFlorian Fainelli  * Broadcom BCM7xxx internal transceivers support.
3b560a58cSFlorian Fainelli  *
483ee102aSDoug Berger  * Copyright (C) 2014-2017 Broadcom
5b560a58cSFlorian Fainelli  *
6b560a58cSFlorian Fainelli  * This program is free software; you can redistribute it and/or
7b560a58cSFlorian Fainelli  * modify it under the terms of the GNU General Public License
8b560a58cSFlorian Fainelli  * as published by the Free Software Foundation; either version
9b560a58cSFlorian Fainelli  * 2 of the License, or (at your option) any later version.
10b560a58cSFlorian Fainelli  */
11b560a58cSFlorian Fainelli 
12b560a58cSFlorian Fainelli #include <linux/module.h>
13b560a58cSFlorian Fainelli #include <linux/phy.h>
14b560a58cSFlorian Fainelli #include <linux/delay.h>
15a1cba561SArun Parameswaran #include "bcm-phy-lib.h"
16b560a58cSFlorian Fainelli #include <linux/bitops.h>
17b560a58cSFlorian Fainelli #include <linux/brcmphy.h>
18b8f9a029SFlorian Fainelli #include <linux/mdio.h>
19b560a58cSFlorian Fainelli 
20b560a58cSFlorian Fainelli /* Broadcom BCM7xxx internal PHY registers */
21b560a58cSFlorian Fainelli 
2283ee102aSDoug Berger /* EPHY only register definitions */
23b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_AUX_CTL	0x10
24b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_FALSE_CAR	0x13
25b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_DISC		0x14
26b560a58cSFlorian Fainelli #define MII_BCM7XXX_AUX_MODE		0x1d
273ccc3055SFlorian Fainelli #define  MII_BCM7XXX_64CLK_MDIO		BIT(12)
28b560a58cSFlorian Fainelli #define MII_BCM7XXX_TEST		0x1f
29b560a58cSFlorian Fainelli #define  MII_BCM7XXX_SHD_MODE_2		BIT(2)
3083ee102aSDoug Berger #define MII_BCM7XXX_SHD_2_ADDR_CTRL	0xe
3183ee102aSDoug Berger #define MII_BCM7XXX_SHD_2_CTRL_STAT	0xf
3283ee102aSDoug Berger #define MII_BCM7XXX_SHD_2_BIAS_TRIM	0x1a
3383ee102aSDoug Berger #define MII_BCM7XXX_SHD_3_AN_EEE_ADV	0x3
3483ee102aSDoug Berger #define MII_BCM7XXX_SHD_3_PCS_CTRL_2	0x6
3583ee102aSDoug Berger #define  MII_BCM7XXX_PCS_CTRL_2_DEF	0x4400
3683ee102aSDoug Berger #define MII_BCM7XXX_SHD_3_AN_STAT	0xb
3783ee102aSDoug Berger #define  MII_BCM7XXX_AN_NULL_MSG_EN	BIT(0)
3883ee102aSDoug Berger #define  MII_BCM7XXX_AN_EEE_EN		BIT(1)
3983ee102aSDoug Berger #define MII_BCM7XXX_SHD_3_EEE_THRESH	0xe
4083ee102aSDoug Berger #define  MII_BCM7XXX_EEE_THRESH_DEF	0x50
4183ee102aSDoug Berger #define MII_BCM7XXX_SHD_3_TL4		0x23
4283ee102aSDoug Berger #define  MII_BCM7XXX_TL4_RST_MSK	(BIT(2) | BIT(1))
43b560a58cSFlorian Fainelli 
44a3622f2cSFlorian Fainelli /* 28nm only register definitions */
45a3622f2cSFlorian Fainelli #define MISC_ADDR(base, channel)	base, channel
46a3622f2cSFlorian Fainelli 
47a3622f2cSFlorian Fainelli #define DSP_TAP10			MISC_ADDR(0x0a, 0)
48a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_1			MISC_ADDR(0x32, 1)
49a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_2			MISC_ADDR(0x32, 2)
50a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_4			MISC_ADDR(0x33, 0)
51a3622f2cSFlorian Fainelli 
52a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_0			MISC_ADDR(0x38, 0)
53a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_1			MISC_ADDR(0x38, 1)
54a490631fSFlorian Fainelli #define AFE_RXCONFIG_2			MISC_ADDR(0x38, 2)
55a3622f2cSFlorian Fainelli #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
56a3622f2cSFlorian Fainelli #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
57a490631fSFlorian Fainelli #define AFE_VDCA_ICTRL_0		MISC_ADDR(0x39, 1)
58a490631fSFlorian Fainelli #define AFE_VDAC_OTHERS_0		MISC_ADDR(0x39, 3)
59a3622f2cSFlorian Fainelli #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
60a3622f2cSFlorian Fainelli 
61b23ce9e8SFlorian Fainelli struct bcm7xxx_phy_priv {
62b23ce9e8SFlorian Fainelli 	u64	*stats;
63b23ce9e8SFlorian Fainelli };
64b23ce9e8SFlorian Fainelli 
659c41f2baSFlorian Fainelli static void r_rc_cal_reset(struct phy_device *phydev)
669c41f2baSFlorian Fainelli {
679c41f2baSFlorian Fainelli 	/* Reset R_CAL/RC_CAL Engine */
68a1cba561SArun Parameswaran 	bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
699c41f2baSFlorian Fainelli 
709c41f2baSFlorian Fainelli 	/* Disable Reset R_AL/RC_CAL Engine */
71a1cba561SArun Parameswaran 	bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
729c41f2baSFlorian Fainelli }
739c41f2baSFlorian Fainelli 
742a9df742SFlorian Fainelli static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
75b560a58cSFlorian Fainelli {
76b560a58cSFlorian Fainelli 	/* Increase VCO range to prevent unlocking problem of PLL at low
77b560a58cSFlorian Fainelli 	 * temp
78b560a58cSFlorian Fainelli 	 */
79a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
80b560a58cSFlorian Fainelli 
81b560a58cSFlorian Fainelli 	/* Change Ki to 011 */
82a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
83b560a58cSFlorian Fainelli 
84b560a58cSFlorian Fainelli 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
85b560a58cSFlorian Fainelli 	 * to 111
86b560a58cSFlorian Fainelli 	 */
87a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
88b560a58cSFlorian Fainelli 
89b560a58cSFlorian Fainelli 	/* Adjust bias current trim by -3 */
90a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
91b560a58cSFlorian Fainelli 
92b560a58cSFlorian Fainelli 	/* Switch to CORE_BASE1E */
939200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
94b560a58cSFlorian Fainelli 
959c41f2baSFlorian Fainelli 	r_rc_cal_reset(phydev);
96b560a58cSFlorian Fainelli 
979918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_0 */
98a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
999918542eSFlorian Fainelli 
1009918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_1 */
101a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
1029918542eSFlorian Fainelli 
1039918542eSFlorian Fainelli 	/* write AFE_RX_LP_COUNTER */
104a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
1059918542eSFlorian Fainelli 
1069918542eSFlorian Fainelli 	/* write AFE_HPF_TRIM_OTHERS */
107a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
1089918542eSFlorian Fainelli 
1099918542eSFlorian Fainelli 	/* write AFTE_TX_CONFIG */
110a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
1119918542eSFlorian Fainelli 
112b560a58cSFlorian Fainelli 	return 0;
113b560a58cSFlorian Fainelli }
114b560a58cSFlorian Fainelli 
115a490631fSFlorian Fainelli static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
116a490631fSFlorian Fainelli {
117a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_0 */
118a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
119a490631fSFlorian Fainelli 
120a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_1 */
121a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
122a490631fSFlorian Fainelli 
123a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
124a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
125a490631fSFlorian Fainelli 
126a490631fSFlorian Fainelli 	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
127a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
128a490631fSFlorian Fainelli 
1296da8253bSFlorian Fainelli 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
130a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
131a490631fSFlorian Fainelli 
132a490631fSFlorian Fainelli 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
133a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
134a490631fSFlorian Fainelli 
135a490631fSFlorian Fainelli 	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
136a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
137a490631fSFlorian Fainelli 
138a490631fSFlorian Fainelli 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
139a490631fSFlorian Fainelli 	 * offset for HT=0 code
140a490631fSFlorian Fainelli 	 */
141a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
142a490631fSFlorian Fainelli 
143a490631fSFlorian Fainelli 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
1449200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
145a490631fSFlorian Fainelli 
146a490631fSFlorian Fainelli 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
147a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
148a490631fSFlorian Fainelli 
149a490631fSFlorian Fainelli 	/* Reset R_CAL/RC_CAL engine */
150a490631fSFlorian Fainelli 	r_rc_cal_reset(phydev);
151a490631fSFlorian Fainelli 
152a490631fSFlorian Fainelli 	return 0;
153a490631fSFlorian Fainelli }
154a490631fSFlorian Fainelli 
1550c2fdc25SFlorian Fainelli static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
1560c2fdc25SFlorian Fainelli {
1570c2fdc25SFlorian Fainelli 	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
158a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
1590c2fdc25SFlorian Fainelli 
1606da8253bSFlorian Fainelli 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
161a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
1626da8253bSFlorian Fainelli 
1630c2fdc25SFlorian Fainelli 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
164a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
1650c2fdc25SFlorian Fainelli 
1660c2fdc25SFlorian Fainelli 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
1670c2fdc25SFlorian Fainelli 	 * offset for HT=0 code
1680c2fdc25SFlorian Fainelli 	 */
169a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
1700c2fdc25SFlorian Fainelli 
1710c2fdc25SFlorian Fainelli 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
1729200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
1730c2fdc25SFlorian Fainelli 
1740c2fdc25SFlorian Fainelli 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
175a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
1760c2fdc25SFlorian Fainelli 
1770c2fdc25SFlorian Fainelli 	/* Reset R_CAL/RC_CAL engine */
1780c2fdc25SFlorian Fainelli 	r_rc_cal_reset(phydev);
1790c2fdc25SFlorian Fainelli 
1800c2fdc25SFlorian Fainelli 	return 0;
1810c2fdc25SFlorian Fainelli }
1820c2fdc25SFlorian Fainelli 
183039a7b85SFlorian Fainelli static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)
184039a7b85SFlorian Fainelli {
185039a7b85SFlorian Fainelli 	/* +1 RC_CAL codes for RL centering for both LT and HT conditions */
186039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);
187039a7b85SFlorian Fainelli 
188039a7b85SFlorian Fainelli 	/* Cut master bias current by 2% to compensate for RC_CAL offset */
189039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);
190039a7b85SFlorian Fainelli 
191039a7b85SFlorian Fainelli 	/* Improve hybrid leakage */
192039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);
193039a7b85SFlorian Fainelli 
194039a7b85SFlorian Fainelli 	/* Change rx_on_tune 8 to 0xf */
195039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);
196039a7b85SFlorian Fainelli 
197039a7b85SFlorian Fainelli 	/* Change 100Tx EEE bandwidth */
198039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);
199039a7b85SFlorian Fainelli 
200039a7b85SFlorian Fainelli 	/* Enable ffe zero detection for Vitesse interoperability */
201039a7b85SFlorian Fainelli 	bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);
202039a7b85SFlorian Fainelli 
203039a7b85SFlorian Fainelli 	r_rc_cal_reset(phydev);
204039a7b85SFlorian Fainelli 
205039a7b85SFlorian Fainelli 	return 0;
206039a7b85SFlorian Fainelli }
207039a7b85SFlorian Fainelli 
208b560a58cSFlorian Fainelli static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
209b560a58cSFlorian Fainelli {
210d8ebfed3SFlorian Fainelli 	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
211d8ebfed3SFlorian Fainelli 	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
212db88816bSFlorian Fainelli 	u8 count;
213d8ebfed3SFlorian Fainelli 	int ret = 0;
214b560a58cSFlorian Fainelli 
215039a7b85SFlorian Fainelli 	/* Newer devices have moved the revision information back into a
216039a7b85SFlorian Fainelli 	 * standard location in MII_PHYS_ID[23]
217039a7b85SFlorian Fainelli 	 */
218039a7b85SFlorian Fainelli 	if (rev == 0)
219039a7b85SFlorian Fainelli 		rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
220039a7b85SFlorian Fainelli 
2216ec259c1SFlorian Fainelli 	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
22284eff6d1SAndrew Lunn 		     phydev_name(phydev), phydev->drv->name, rev, patch);
223d8ebfed3SFlorian Fainelli 
2248e346e15SFlorian Fainelli 	/* Dummy read to a register to workaround an issue upon reset where the
2258e346e15SFlorian Fainelli 	 * internal inverter may not allow the first MDIO transaction to pass
2268e346e15SFlorian Fainelli 	 * the MDIO management controller and make us return 0xffff for such
2278e346e15SFlorian Fainelli 	 * reads.
2288e346e15SFlorian Fainelli 	 */
2298e346e15SFlorian Fainelli 	phy_read(phydev, MII_BMSR);
2308e346e15SFlorian Fainelli 
231d8ebfed3SFlorian Fainelli 	switch (rev) {
232d8ebfed3SFlorian Fainelli 	case 0xb0:
2332a9df742SFlorian Fainelli 		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
234d8ebfed3SFlorian Fainelli 		break;
235a490631fSFlorian Fainelli 	case 0xd0:
236a490631fSFlorian Fainelli 		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
237a490631fSFlorian Fainelli 		break;
2380c2fdc25SFlorian Fainelli 	case 0xe0:
2390c2fdc25SFlorian Fainelli 	case 0xf0:
24060efff0cSFlorian Fainelli 	/* Rev G0 introduces a roll over */
24160efff0cSFlorian Fainelli 	case 0x10:
2420c2fdc25SFlorian Fainelli 		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
2430c2fdc25SFlorian Fainelli 		break;
244039a7b85SFlorian Fainelli 	case 0x01:
245039a7b85SFlorian Fainelli 		ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
246039a7b85SFlorian Fainelli 		break;
247d8ebfed3SFlorian Fainelli 	default:
248d8ebfed3SFlorian Fainelli 		break;
249d8ebfed3SFlorian Fainelli 	}
250d8ebfed3SFlorian Fainelli 
2519df54ddaSFlorian Fainelli 	if (ret)
2529df54ddaSFlorian Fainelli 		return ret;
2539df54ddaSFlorian Fainelli 
254db88816bSFlorian Fainelli 	ret = bcm_phy_downshift_get(phydev, &count);
255db88816bSFlorian Fainelli 	if (ret)
256db88816bSFlorian Fainelli 		return ret;
257db88816bSFlorian Fainelli 
258db88816bSFlorian Fainelli 	/* Only enable EEE if Wirespeed/downshift is disabled */
259db88816bSFlorian Fainelli 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
260b8f9a029SFlorian Fainelli 	if (ret)
261b8f9a029SFlorian Fainelli 		return ret;
262b8f9a029SFlorian Fainelli 
263a1cba561SArun Parameswaran 	return bcm_phy_enable_apd(phydev, true);
264b560a58cSFlorian Fainelli }
265b560a58cSFlorian Fainelli 
2664fd14e0bSFlorian Fainelli static int bcm7xxx_28nm_resume(struct phy_device *phydev)
2674fd14e0bSFlorian Fainelli {
2684fd14e0bSFlorian Fainelli 	int ret;
2694fd14e0bSFlorian Fainelli 
2704fd14e0bSFlorian Fainelli 	/* Re-apply workarounds coming out suspend/resume */
2714fd14e0bSFlorian Fainelli 	ret = bcm7xxx_28nm_config_init(phydev);
2724fd14e0bSFlorian Fainelli 	if (ret)
2734fd14e0bSFlorian Fainelli 		return ret;
2744fd14e0bSFlorian Fainelli 
2754fd14e0bSFlorian Fainelli 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
2764fd14e0bSFlorian Fainelli 	 * or "hub" compliant advertised mode, fix that. This does not
2774fd14e0bSFlorian Fainelli 	 * cause any problems with the PHY library since genphy_config_aneg()
2784fd14e0bSFlorian Fainelli 	 * gracefully handles auto-negotiated and forced modes.
2794fd14e0bSFlorian Fainelli 	 */
2804fd14e0bSFlorian Fainelli 	return genphy_config_aneg(phydev);
2814fd14e0bSFlorian Fainelli }
2824fd14e0bSFlorian Fainelli 
283b560a58cSFlorian Fainelli static int phy_set_clr_bits(struct phy_device *dev, int location,
284b560a58cSFlorian Fainelli 					int set_mask, int clr_mask)
285b560a58cSFlorian Fainelli {
286b560a58cSFlorian Fainelli 	int v, ret;
287b560a58cSFlorian Fainelli 
288b560a58cSFlorian Fainelli 	v = phy_read(dev, location);
289b560a58cSFlorian Fainelli 	if (v < 0)
290b560a58cSFlorian Fainelli 		return v;
291b560a58cSFlorian Fainelli 
292b560a58cSFlorian Fainelli 	v &= ~clr_mask;
293b560a58cSFlorian Fainelli 	v |= set_mask;
294b560a58cSFlorian Fainelli 
295b560a58cSFlorian Fainelli 	ret = phy_write(dev, location, v);
296b560a58cSFlorian Fainelli 	if (ret < 0)
297b560a58cSFlorian Fainelli 		return ret;
298b560a58cSFlorian Fainelli 
299b560a58cSFlorian Fainelli 	return v;
300b560a58cSFlorian Fainelli }
301b560a58cSFlorian Fainelli 
30283ee102aSDoug Berger static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev)
30383ee102aSDoug Berger {
30483ee102aSDoug Berger 	int ret;
30583ee102aSDoug Berger 
30683ee102aSDoug Berger 	/* set shadow mode 2 */
30783ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
30883ee102aSDoug Berger 			       MII_BCM7XXX_SHD_MODE_2, 0);
30983ee102aSDoug Berger 	if (ret < 0)
31083ee102aSDoug Berger 		return ret;
31183ee102aSDoug Berger 
31283ee102aSDoug Berger 	/* Set current trim values INT_trim = -1, Ext_trim =0 */
31383ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, 0x3BE0);
31483ee102aSDoug Berger 	if (ret < 0)
31583ee102aSDoug Berger 		goto reset_shadow_mode;
31683ee102aSDoug Berger 
31783ee102aSDoug Berger 	/* Cal reset */
31883ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
31983ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_TL4);
32083ee102aSDoug Berger 	if (ret < 0)
32183ee102aSDoug Berger 		goto reset_shadow_mode;
32283ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
32383ee102aSDoug Berger 			       MII_BCM7XXX_TL4_RST_MSK, 0);
32483ee102aSDoug Berger 	if (ret < 0)
32583ee102aSDoug Berger 		goto reset_shadow_mode;
32683ee102aSDoug Berger 
32783ee102aSDoug Berger 	/* Cal reset disable */
32883ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
32983ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_TL4);
33083ee102aSDoug Berger 	if (ret < 0)
33183ee102aSDoug Berger 		goto reset_shadow_mode;
33283ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
33383ee102aSDoug Berger 			       0, MII_BCM7XXX_TL4_RST_MSK);
33483ee102aSDoug Berger 	if (ret < 0)
33583ee102aSDoug Berger 		goto reset_shadow_mode;
33683ee102aSDoug Berger 
33783ee102aSDoug Berger reset_shadow_mode:
33883ee102aSDoug Berger 	/* reset shadow mode 2 */
33983ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,
34083ee102aSDoug Berger 			       MII_BCM7XXX_SHD_MODE_2);
34183ee102aSDoug Berger 	if (ret < 0)
34283ee102aSDoug Berger 		return ret;
34383ee102aSDoug Berger 
34483ee102aSDoug Berger 	return 0;
34583ee102aSDoug Berger }
34683ee102aSDoug Berger 
34783ee102aSDoug Berger /* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */
34883ee102aSDoug Berger static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev)
34983ee102aSDoug Berger {
35083ee102aSDoug Berger 	int ret;
35183ee102aSDoug Berger 
35283ee102aSDoug Berger 	/* set shadow mode 1 */
35383ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST,
35483ee102aSDoug Berger 			       MII_BRCM_FET_BT_SRE, 0);
35583ee102aSDoug Berger 	if (ret < 0)
35683ee102aSDoug Berger 		return ret;
35783ee102aSDoug Berger 
35883ee102aSDoug Berger 	/* Enable auto-power down */
35983ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
36083ee102aSDoug Berger 			       MII_BRCM_FET_SHDW_AS2_APDE, 0);
36183ee102aSDoug Berger 	if (ret < 0)
36283ee102aSDoug Berger 		return ret;
36383ee102aSDoug Berger 
36483ee102aSDoug Berger 	/* reset shadow mode 1 */
36583ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, 0,
36683ee102aSDoug Berger 			       MII_BRCM_FET_BT_SRE);
36783ee102aSDoug Berger 	if (ret < 0)
36883ee102aSDoug Berger 		return ret;
36983ee102aSDoug Berger 
37083ee102aSDoug Berger 	return 0;
37183ee102aSDoug Berger }
37283ee102aSDoug Berger 
37383ee102aSDoug Berger static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev)
37483ee102aSDoug Berger {
37583ee102aSDoug Berger 	int ret;
37683ee102aSDoug Berger 
37783ee102aSDoug Berger 	/* set shadow mode 2 */
37883ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
37983ee102aSDoug Berger 			       MII_BCM7XXX_SHD_MODE_2, 0);
38083ee102aSDoug Berger 	if (ret < 0)
38183ee102aSDoug Berger 		return ret;
38283ee102aSDoug Berger 
38383ee102aSDoug Berger 	/* Advertise supported modes */
38483ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
38583ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_AN_EEE_ADV);
38683ee102aSDoug Berger 	if (ret < 0)
38783ee102aSDoug Berger 		goto reset_shadow_mode;
38883ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
38983ee102aSDoug Berger 			MDIO_EEE_100TX);
39083ee102aSDoug Berger 	if (ret < 0)
39183ee102aSDoug Berger 		goto reset_shadow_mode;
39283ee102aSDoug Berger 
39383ee102aSDoug Berger 	/* Restore Defaults */
39483ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
39583ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_PCS_CTRL_2);
39683ee102aSDoug Berger 	if (ret < 0)
39783ee102aSDoug Berger 		goto reset_shadow_mode;
39883ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
39983ee102aSDoug Berger 			MII_BCM7XXX_PCS_CTRL_2_DEF);
40083ee102aSDoug Berger 	if (ret < 0)
40183ee102aSDoug Berger 		goto reset_shadow_mode;
40283ee102aSDoug Berger 
40383ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
40483ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_EEE_THRESH);
40583ee102aSDoug Berger 	if (ret < 0)
40683ee102aSDoug Berger 		goto reset_shadow_mode;
40783ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
40883ee102aSDoug Berger 			MII_BCM7XXX_EEE_THRESH_DEF);
40983ee102aSDoug Berger 	if (ret < 0)
41083ee102aSDoug Berger 		goto reset_shadow_mode;
41183ee102aSDoug Berger 
41283ee102aSDoug Berger 	/* Enable EEE autonegotiation */
41383ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL,
41483ee102aSDoug Berger 			MII_BCM7XXX_SHD_3_AN_STAT);
41583ee102aSDoug Berger 	if (ret < 0)
41683ee102aSDoug Berger 		goto reset_shadow_mode;
41783ee102aSDoug Berger 	ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT,
41883ee102aSDoug Berger 			(MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN));
41983ee102aSDoug Berger 	if (ret < 0)
42083ee102aSDoug Berger 		goto reset_shadow_mode;
42183ee102aSDoug Berger 
42283ee102aSDoug Berger reset_shadow_mode:
42383ee102aSDoug Berger 	/* reset shadow mode 2 */
42483ee102aSDoug Berger 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0,
42583ee102aSDoug Berger 			       MII_BCM7XXX_SHD_MODE_2);
42683ee102aSDoug Berger 	if (ret < 0)
42783ee102aSDoug Berger 		return ret;
42883ee102aSDoug Berger 
42983ee102aSDoug Berger 	/* Restart autoneg */
43083ee102aSDoug Berger 	phy_write(phydev, MII_BMCR,
43183ee102aSDoug Berger 		  (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART));
43283ee102aSDoug Berger 
43383ee102aSDoug Berger 	return 0;
43483ee102aSDoug Berger }
43583ee102aSDoug Berger 
43683ee102aSDoug Berger static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev)
43783ee102aSDoug Berger {
43883ee102aSDoug Berger 	u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
43983ee102aSDoug Berger 	int ret = 0;
44083ee102aSDoug Berger 
44183ee102aSDoug Berger 	pr_info_once("%s: %s PHY revision: 0x%02x\n",
44283ee102aSDoug Berger 		     phydev_name(phydev), phydev->drv->name, rev);
44383ee102aSDoug Berger 
44483ee102aSDoug Berger 	/* Dummy read to a register to workaround a possible issue upon reset
44583ee102aSDoug Berger 	 * where the internal inverter may not allow the first MDIO transaction
44683ee102aSDoug Berger 	 * to pass the MDIO management controller and make us return 0xffff for
44783ee102aSDoug Berger 	 * such reads.
44883ee102aSDoug Berger 	 */
44983ee102aSDoug Berger 	phy_read(phydev, MII_BMSR);
45083ee102aSDoug Berger 
45183ee102aSDoug Berger 	/* Apply AFE software work-around if necessary */
45283ee102aSDoug Berger 	if (rev == 0x01) {
45383ee102aSDoug Berger 		ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev);
45483ee102aSDoug Berger 		if (ret)
45583ee102aSDoug Berger 			return ret;
45683ee102aSDoug Berger 	}
45783ee102aSDoug Berger 
45883ee102aSDoug Berger 	ret = bcm7xxx_28nm_ephy_eee_enable(phydev);
45983ee102aSDoug Berger 	if (ret)
46083ee102aSDoug Berger 		return ret;
46183ee102aSDoug Berger 
46283ee102aSDoug Berger 	return bcm7xxx_28nm_ephy_apd_enable(phydev);
46383ee102aSDoug Berger }
46483ee102aSDoug Berger 
46583ee102aSDoug Berger static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev)
46683ee102aSDoug Berger {
46783ee102aSDoug Berger 	int ret;
46883ee102aSDoug Berger 
46983ee102aSDoug Berger 	/* Re-apply workarounds coming out suspend/resume */
47083ee102aSDoug Berger 	ret = bcm7xxx_28nm_ephy_config_init(phydev);
47183ee102aSDoug Berger 	if (ret)
47283ee102aSDoug Berger 		return ret;
47383ee102aSDoug Berger 
47483ee102aSDoug Berger 	return genphy_config_aneg(phydev);
47583ee102aSDoug Berger }
47683ee102aSDoug Berger 
477b560a58cSFlorian Fainelli static int bcm7xxx_config_init(struct phy_device *phydev)
478b560a58cSFlorian Fainelli {
479b560a58cSFlorian Fainelli 	int ret;
480b560a58cSFlorian Fainelli 
481b560a58cSFlorian Fainelli 	/* Enable 64 clock MDIO */
4823ccc3055SFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);
483b560a58cSFlorian Fainelli 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
484b560a58cSFlorian Fainelli 
485b560a58cSFlorian Fainelli 	/* set shadow mode 2 */
486b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
487b560a58cSFlorian Fainelli 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
488b560a58cSFlorian Fainelli 	if (ret < 0)
489b560a58cSFlorian Fainelli 		return ret;
490b560a58cSFlorian Fainelli 
491b560a58cSFlorian Fainelli 	/* set iddq_clkbias */
492b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
493b560a58cSFlorian Fainelli 	udelay(10);
494b560a58cSFlorian Fainelli 
495b560a58cSFlorian Fainelli 	/* reset iddq_clkbias */
496b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
497b560a58cSFlorian Fainelli 
498b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
499b560a58cSFlorian Fainelli 
500b560a58cSFlorian Fainelli 	/* reset shadow mode 2 */
50150d89980SFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);
502b560a58cSFlorian Fainelli 	if (ret < 0)
503b560a58cSFlorian Fainelli 		return ret;
504b560a58cSFlorian Fainelli 
505b560a58cSFlorian Fainelli 	return 0;
506b560a58cSFlorian Fainelli }
507b560a58cSFlorian Fainelli 
508b560a58cSFlorian Fainelli /* Workaround for putting the PHY in IDDQ mode, required
50982c084f5SFlorian Fainelli  * for all BCM7XXX 40nm and 65nm PHYs
510b560a58cSFlorian Fainelli  */
511b560a58cSFlorian Fainelli static int bcm7xxx_suspend(struct phy_device *phydev)
512b560a58cSFlorian Fainelli {
513b560a58cSFlorian Fainelli 	int ret;
51433c81821SColin Ian King 	static const struct bcm7xxx_regs {
515b560a58cSFlorian Fainelli 		int reg;
516b560a58cSFlorian Fainelli 		u16 value;
517b560a58cSFlorian Fainelli 	} bcm7xxx_suspend_cfg[] = {
518b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x008b },
519b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
520b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
521b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000f },
522b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
523b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000b },
524b560a58cSFlorian Fainelli 	};
525b560a58cSFlorian Fainelli 	unsigned int i;
526b560a58cSFlorian Fainelli 
527b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
528b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
529b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].reg,
530b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].value);
531b560a58cSFlorian Fainelli 		if (ret)
532b560a58cSFlorian Fainelli 			return ret;
533b560a58cSFlorian Fainelli 	}
534b560a58cSFlorian Fainelli 
535b560a58cSFlorian Fainelli 	return 0;
536b560a58cSFlorian Fainelli }
537b560a58cSFlorian Fainelli 
538db88816bSFlorian Fainelli static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
539db88816bSFlorian Fainelli 				    struct ethtool_tunable *tuna,
540db88816bSFlorian Fainelli 				    void *data)
541db88816bSFlorian Fainelli {
542db88816bSFlorian Fainelli 	switch (tuna->id) {
543db88816bSFlorian Fainelli 	case ETHTOOL_PHY_DOWNSHIFT:
544db88816bSFlorian Fainelli 		return bcm_phy_downshift_get(phydev, (u8 *)data);
545db88816bSFlorian Fainelli 	default:
546db88816bSFlorian Fainelli 		return -EOPNOTSUPP;
547db88816bSFlorian Fainelli 	}
548db88816bSFlorian Fainelli }
549db88816bSFlorian Fainelli 
550db88816bSFlorian Fainelli static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
551db88816bSFlorian Fainelli 				    struct ethtool_tunable *tuna,
552db88816bSFlorian Fainelli 				    const void *data)
553db88816bSFlorian Fainelli {
554db88816bSFlorian Fainelli 	u8 count = *(u8 *)data;
555db88816bSFlorian Fainelli 	int ret;
556db88816bSFlorian Fainelli 
557db88816bSFlorian Fainelli 	switch (tuna->id) {
558db88816bSFlorian Fainelli 	case ETHTOOL_PHY_DOWNSHIFT:
559db88816bSFlorian Fainelli 		ret = bcm_phy_downshift_set(phydev, count);
560db88816bSFlorian Fainelli 		break;
561db88816bSFlorian Fainelli 	default:
562db88816bSFlorian Fainelli 		return -EOPNOTSUPP;
563db88816bSFlorian Fainelli 	}
564db88816bSFlorian Fainelli 
565db88816bSFlorian Fainelli 	if (ret)
566db88816bSFlorian Fainelli 		return ret;
567db88816bSFlorian Fainelli 
568cc1122b0SColin Ian King 	/* Disable EEE advertisement since this prevents the PHY
569db88816bSFlorian Fainelli 	 * from successfully linking up, trigger auto-negotiation restart
570db88816bSFlorian Fainelli 	 * to let the MAC decide what to do.
571db88816bSFlorian Fainelli 	 */
572db88816bSFlorian Fainelli 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
573db88816bSFlorian Fainelli 	if (ret)
574db88816bSFlorian Fainelli 		return ret;
575db88816bSFlorian Fainelli 
576db88816bSFlorian Fainelli 	return genphy_restart_aneg(phydev);
577db88816bSFlorian Fainelli }
578db88816bSFlorian Fainelli 
579b23ce9e8SFlorian Fainelli static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
580b23ce9e8SFlorian Fainelli 				       struct ethtool_stats *stats, u64 *data)
581b23ce9e8SFlorian Fainelli {
582b23ce9e8SFlorian Fainelli 	struct bcm7xxx_phy_priv *priv = phydev->priv;
583b23ce9e8SFlorian Fainelli 
584b23ce9e8SFlorian Fainelli 	bcm_phy_get_stats(phydev, priv->stats, stats, data);
585b23ce9e8SFlorian Fainelli }
586b23ce9e8SFlorian Fainelli 
587b23ce9e8SFlorian Fainelli static int bcm7xxx_28nm_probe(struct phy_device *phydev)
588b23ce9e8SFlorian Fainelli {
589b23ce9e8SFlorian Fainelli 	struct bcm7xxx_phy_priv *priv;
590b23ce9e8SFlorian Fainelli 
591b23ce9e8SFlorian Fainelli 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
592b23ce9e8SFlorian Fainelli 	if (!priv)
593b23ce9e8SFlorian Fainelli 		return -ENOMEM;
594b23ce9e8SFlorian Fainelli 
595b23ce9e8SFlorian Fainelli 	phydev->priv = priv;
596b23ce9e8SFlorian Fainelli 
597b23ce9e8SFlorian Fainelli 	priv->stats = devm_kcalloc(&phydev->mdio.dev,
598b23ce9e8SFlorian Fainelli 				   bcm_phy_get_sset_count(phydev), sizeof(u64),
599b23ce9e8SFlorian Fainelli 				   GFP_KERNEL);
600b23ce9e8SFlorian Fainelli 	if (!priv->stats)
601b23ce9e8SFlorian Fainelli 		return -ENOMEM;
602b23ce9e8SFlorian Fainelli 
603b23ce9e8SFlorian Fainelli 	return 0;
604b23ce9e8SFlorian Fainelli }
605b23ce9e8SFlorian Fainelli 
606153df3c7SFlorian Fainelli #define BCM7XXX_28NM_GPHY(_oui, _name)					\
607153df3c7SFlorian Fainelli {									\
608153df3c7SFlorian Fainelli 	.phy_id		= (_oui),					\
609153df3c7SFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,					\
610153df3c7SFlorian Fainelli 	.name		= _name,					\
611529ed127STimur Tabi 	.features	= PHY_GBIT_FEATURES,				\
612153df3c7SFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,				\
6132a9df742SFlorian Fainelli 	.config_init	= bcm7xxx_28nm_config_init,			\
614153df3c7SFlorian Fainelli 	.resume		= bcm7xxx_28nm_resume,				\
615db88816bSFlorian Fainelli 	.get_tunable	= bcm7xxx_28nm_get_tunable,			\
616db88816bSFlorian Fainelli 	.set_tunable	= bcm7xxx_28nm_set_tunable,			\
617b23ce9e8SFlorian Fainelli 	.get_sset_count	= bcm_phy_get_sset_count,			\
618b23ce9e8SFlorian Fainelli 	.get_strings	= bcm_phy_get_strings,				\
619b23ce9e8SFlorian Fainelli 	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\
620b23ce9e8SFlorian Fainelli 	.probe		= bcm7xxx_28nm_probe,				\
621153df3c7SFlorian Fainelli }
622153df3c7SFlorian Fainelli 
62383ee102aSDoug Berger #define BCM7XXX_28NM_EPHY(_oui, _name)					\
62483ee102aSDoug Berger {									\
62583ee102aSDoug Berger 	.phy_id		= (_oui),					\
62683ee102aSDoug Berger 	.phy_id_mask	= 0xfffffff0,					\
62783ee102aSDoug Berger 	.name		= _name,					\
62883ee102aSDoug Berger 	.features	= PHY_BASIC_FEATURES,				\
62983ee102aSDoug Berger 	.flags		= PHY_IS_INTERNAL,				\
63083ee102aSDoug Berger 	.config_init	= bcm7xxx_28nm_ephy_config_init,		\
63183ee102aSDoug Berger 	.resume		= bcm7xxx_28nm_ephy_resume,			\
63283ee102aSDoug Berger 	.get_sset_count	= bcm_phy_get_sset_count,			\
63383ee102aSDoug Berger 	.get_strings	= bcm_phy_get_strings,				\
63483ee102aSDoug Berger 	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\
63583ee102aSDoug Berger 	.probe		= bcm7xxx_28nm_probe,				\
63683ee102aSDoug Berger }
63783ee102aSDoug Berger 
6383125c081SFlorian Fainelli #define BCM7XXX_40NM_EPHY(_oui, _name)					\
6393125c081SFlorian Fainelli {									\
6403125c081SFlorian Fainelli 	.phy_id         = (_oui),					\
6413125c081SFlorian Fainelli 	.phy_id_mask    = 0xfffffff0,					\
6423125c081SFlorian Fainelli 	.name           = _name,					\
643529ed127STimur Tabi 	.features       = PHY_BASIC_FEATURES,				\
6443125c081SFlorian Fainelli 	.flags          = PHY_IS_INTERNAL,				\
6453125c081SFlorian Fainelli 	.config_init    = bcm7xxx_config_init,				\
6463125c081SFlorian Fainelli 	.suspend        = bcm7xxx_suspend,				\
6473125c081SFlorian Fainelli 	.resume         = bcm7xxx_config_init,				\
6483125c081SFlorian Fainelli }
6493125c081SFlorian Fainelli 
650b560a58cSFlorian Fainelli static struct phy_driver bcm7xxx_driver[] = {
651430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
65283ee102aSDoug Berger 	BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"),
65383ee102aSDoug Berger 	BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"),
65483ee102aSDoug Berger 	BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"),
655582d0ac3SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),
656430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
657153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
658b08d46b0SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"),
659153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
66059e33c2bSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
661153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
6624cef191dSJaedon Shin 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),
6634cef191dSJaedon Shin 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),
6643125c081SFlorian Fainelli 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),
6653125c081SFlorian Fainelli 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),
6663125c081SFlorian Fainelli 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),
667b6333531SDavid S. Miller };
668b560a58cSFlorian Fainelli 
669b560a58cSFlorian Fainelli static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
670430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7250, 0xfffffff0, },
67183ee102aSDoug Berger 	{ PHY_ID_BCM7260, 0xfffffff0, },
67283ee102aSDoug Berger 	{ PHY_ID_BCM7268, 0xfffffff0, },
67383ee102aSDoug Berger 	{ PHY_ID_BCM7271, 0xfffffff0, },
674582d0ac3SFlorian Fainelli 	{ PHY_ID_BCM7278, 0xfffffff0, },
675430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7364, 0xfffffff0, },
676b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7366, 0xfffffff0, },
6774cef191dSJaedon Shin 	{ PHY_ID_BCM7346, 0xfffffff0, },
6784cef191dSJaedon Shin 	{ PHY_ID_BCM7362, 0xfffffff0, },
679d068b02cSPetri Gynther 	{ PHY_ID_BCM7425, 0xfffffff0, },
680d068b02cSPetri Gynther 	{ PHY_ID_BCM7429, 0xfffffff0, },
681b08d46b0SFlorian Fainelli 	{ PHY_ID_BCM74371, 0xfffffff0, },
682b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7439, 0xfffffff0, },
6839458ceabSFlorian Fainelli 	{ PHY_ID_BCM7435, 0xfffffff0, },
684b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7445, 0xfffffff0, },
685b560a58cSFlorian Fainelli 	{ }
686b560a58cSFlorian Fainelli };
687b560a58cSFlorian Fainelli 
68850fd7150SJohan Hovold module_phy_driver(bcm7xxx_driver);
689b560a58cSFlorian Fainelli 
690b560a58cSFlorian Fainelli MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
691b560a58cSFlorian Fainelli 
692b560a58cSFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
693b560a58cSFlorian Fainelli MODULE_LICENSE("GPL");
694b560a58cSFlorian Fainelli MODULE_AUTHOR("Broadcom Corporation");
695