xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision 84eff6d1)
1b560a58cSFlorian Fainelli /*
2b560a58cSFlorian Fainelli  * Broadcom BCM7xxx internal transceivers support.
3b560a58cSFlorian Fainelli  *
4b560a58cSFlorian Fainelli  * Copyright (C) 2014, Broadcom Corporation
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 
22b560a58cSFlorian Fainelli /* 40nm 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
27b560a58cSFlorian Fainelli #define  MII_BCM7XX_64CLK_MDIO		BIT(12)
28b560a58cSFlorian Fainelli #define MII_BCM7XXX_TEST		0x1f
29b560a58cSFlorian Fainelli #define  MII_BCM7XXX_SHD_MODE_2		BIT(2)
30b560a58cSFlorian Fainelli 
31a3622f2cSFlorian Fainelli /* 28nm only register definitions */
32a3622f2cSFlorian Fainelli #define MISC_ADDR(base, channel)	base, channel
33a3622f2cSFlorian Fainelli 
34a3622f2cSFlorian Fainelli #define DSP_TAP10			MISC_ADDR(0x0a, 0)
35a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_1			MISC_ADDR(0x32, 1)
36a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_2			MISC_ADDR(0x32, 2)
37a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_4			MISC_ADDR(0x33, 0)
38a3622f2cSFlorian Fainelli 
39a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_0			MISC_ADDR(0x38, 0)
40a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_1			MISC_ADDR(0x38, 1)
41a490631fSFlorian Fainelli #define AFE_RXCONFIG_2			MISC_ADDR(0x38, 2)
42a3622f2cSFlorian Fainelli #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
43a3622f2cSFlorian Fainelli #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
44a490631fSFlorian Fainelli #define AFE_VDCA_ICTRL_0		MISC_ADDR(0x39, 1)
45a490631fSFlorian Fainelli #define AFE_VDAC_OTHERS_0		MISC_ADDR(0x39, 3)
46a3622f2cSFlorian Fainelli #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
47a3622f2cSFlorian Fainelli 
489c41f2baSFlorian Fainelli static void r_rc_cal_reset(struct phy_device *phydev)
499c41f2baSFlorian Fainelli {
509c41f2baSFlorian Fainelli 	/* Reset R_CAL/RC_CAL Engine */
51a1cba561SArun Parameswaran 	bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
529c41f2baSFlorian Fainelli 
539c41f2baSFlorian Fainelli 	/* Disable Reset R_AL/RC_CAL Engine */
54a1cba561SArun Parameswaran 	bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
559c41f2baSFlorian Fainelli }
569c41f2baSFlorian Fainelli 
572a9df742SFlorian Fainelli static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
58b560a58cSFlorian Fainelli {
59b560a58cSFlorian Fainelli 	/* Increase VCO range to prevent unlocking problem of PLL at low
60b560a58cSFlorian Fainelli 	 * temp
61b560a58cSFlorian Fainelli 	 */
62a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
63b560a58cSFlorian Fainelli 
64b560a58cSFlorian Fainelli 	/* Change Ki to 011 */
65a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
66b560a58cSFlorian Fainelli 
67b560a58cSFlorian Fainelli 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
68b560a58cSFlorian Fainelli 	 * to 111
69b560a58cSFlorian Fainelli 	 */
70a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
71b560a58cSFlorian Fainelli 
72b560a58cSFlorian Fainelli 	/* Adjust bias current trim by -3 */
73a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
74b560a58cSFlorian Fainelli 
75b560a58cSFlorian Fainelli 	/* Switch to CORE_BASE1E */
769200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
77b560a58cSFlorian Fainelli 
789c41f2baSFlorian Fainelli 	r_rc_cal_reset(phydev);
79b560a58cSFlorian Fainelli 
809918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_0 */
81a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
829918542eSFlorian Fainelli 
839918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_1 */
84a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
859918542eSFlorian Fainelli 
869918542eSFlorian Fainelli 	/* write AFE_RX_LP_COUNTER */
87a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
889918542eSFlorian Fainelli 
899918542eSFlorian Fainelli 	/* write AFE_HPF_TRIM_OTHERS */
90a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
919918542eSFlorian Fainelli 
929918542eSFlorian Fainelli 	/* write AFTE_TX_CONFIG */
93a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
949918542eSFlorian Fainelli 
95b560a58cSFlorian Fainelli 	return 0;
96b560a58cSFlorian Fainelli }
97b560a58cSFlorian Fainelli 
98a490631fSFlorian Fainelli static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
99a490631fSFlorian Fainelli {
100a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_0 */
101a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
102a490631fSFlorian Fainelli 
103a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_1 */
104a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
105a490631fSFlorian Fainelli 
106a490631fSFlorian Fainelli 	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
107a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
108a490631fSFlorian Fainelli 
109a490631fSFlorian Fainelli 	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
110a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
111a490631fSFlorian Fainelli 
1126da8253bSFlorian Fainelli 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
113a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
114a490631fSFlorian Fainelli 
115a490631fSFlorian Fainelli 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
116a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
117a490631fSFlorian Fainelli 
118a490631fSFlorian Fainelli 	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
119a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
120a490631fSFlorian Fainelli 
121a490631fSFlorian Fainelli 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
122a490631fSFlorian Fainelli 	 * offset for HT=0 code
123a490631fSFlorian Fainelli 	 */
124a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
125a490631fSFlorian Fainelli 
126a490631fSFlorian Fainelli 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
1279200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
128a490631fSFlorian Fainelli 
129a490631fSFlorian Fainelli 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
130a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
131a490631fSFlorian Fainelli 
132a490631fSFlorian Fainelli 	/* Reset R_CAL/RC_CAL engine */
133a490631fSFlorian Fainelli 	r_rc_cal_reset(phydev);
134a490631fSFlorian Fainelli 
135a490631fSFlorian Fainelli 	return 0;
136a490631fSFlorian Fainelli }
137a490631fSFlorian Fainelli 
1380c2fdc25SFlorian Fainelli static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
1390c2fdc25SFlorian Fainelli {
1400c2fdc25SFlorian Fainelli 	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
141a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
1420c2fdc25SFlorian Fainelli 
1436da8253bSFlorian Fainelli 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
144a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
1456da8253bSFlorian Fainelli 
1460c2fdc25SFlorian Fainelli 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
147a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
1480c2fdc25SFlorian Fainelli 
1490c2fdc25SFlorian Fainelli 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
1500c2fdc25SFlorian Fainelli 	 * offset for HT=0 code
1510c2fdc25SFlorian Fainelli 	 */
152a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
1530c2fdc25SFlorian Fainelli 
1540c2fdc25SFlorian Fainelli 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
1559200c27aSArun Parameswaran 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
1560c2fdc25SFlorian Fainelli 
1570c2fdc25SFlorian Fainelli 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
158a1cba561SArun Parameswaran 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
1590c2fdc25SFlorian Fainelli 
1600c2fdc25SFlorian Fainelli 	/* Reset R_CAL/RC_CAL engine */
1610c2fdc25SFlorian Fainelli 	r_rc_cal_reset(phydev);
1620c2fdc25SFlorian Fainelli 
1630c2fdc25SFlorian Fainelli 	return 0;
1640c2fdc25SFlorian Fainelli }
1650c2fdc25SFlorian Fainelli 
166b560a58cSFlorian Fainelli static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
167b560a58cSFlorian Fainelli {
168d8ebfed3SFlorian Fainelli 	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
169d8ebfed3SFlorian Fainelli 	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
170d8ebfed3SFlorian Fainelli 	int ret = 0;
171b560a58cSFlorian Fainelli 
1726ec259c1SFlorian Fainelli 	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
17384eff6d1SAndrew Lunn 		     phydev_name(phydev), phydev->drv->name, rev, patch);
174d8ebfed3SFlorian Fainelli 
1758e346e15SFlorian Fainelli 	/* Dummy read to a register to workaround an issue upon reset where the
1768e346e15SFlorian Fainelli 	 * internal inverter may not allow the first MDIO transaction to pass
1778e346e15SFlorian Fainelli 	 * the MDIO management controller and make us return 0xffff for such
1788e346e15SFlorian Fainelli 	 * reads.
1798e346e15SFlorian Fainelli 	 */
1808e346e15SFlorian Fainelli 	phy_read(phydev, MII_BMSR);
1818e346e15SFlorian Fainelli 
182d8ebfed3SFlorian Fainelli 	switch (rev) {
183d8ebfed3SFlorian Fainelli 	case 0xb0:
1842a9df742SFlorian Fainelli 		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
185d8ebfed3SFlorian Fainelli 		break;
186a490631fSFlorian Fainelli 	case 0xd0:
187a490631fSFlorian Fainelli 		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
188a490631fSFlorian Fainelli 		break;
1890c2fdc25SFlorian Fainelli 	case 0xe0:
1900c2fdc25SFlorian Fainelli 	case 0xf0:
19160efff0cSFlorian Fainelli 	/* Rev G0 introduces a roll over */
19260efff0cSFlorian Fainelli 	case 0x10:
1930c2fdc25SFlorian Fainelli 		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
1940c2fdc25SFlorian Fainelli 		break;
195d8ebfed3SFlorian Fainelli 	default:
196d8ebfed3SFlorian Fainelli 		break;
197d8ebfed3SFlorian Fainelli 	}
198d8ebfed3SFlorian Fainelli 
1999df54ddaSFlorian Fainelli 	if (ret)
2009df54ddaSFlorian Fainelli 		return ret;
2019df54ddaSFlorian Fainelli 
202a1cba561SArun Parameswaran 	ret = bcm_phy_enable_eee(phydev);
203b8f9a029SFlorian Fainelli 	if (ret)
204b8f9a029SFlorian Fainelli 		return ret;
205b8f9a029SFlorian Fainelli 
206a1cba561SArun Parameswaran 	return bcm_phy_enable_apd(phydev, true);
207b560a58cSFlorian Fainelli }
208b560a58cSFlorian Fainelli 
2094fd14e0bSFlorian Fainelli static int bcm7xxx_28nm_resume(struct phy_device *phydev)
2104fd14e0bSFlorian Fainelli {
2114fd14e0bSFlorian Fainelli 	int ret;
2124fd14e0bSFlorian Fainelli 
2134fd14e0bSFlorian Fainelli 	/* Re-apply workarounds coming out suspend/resume */
2144fd14e0bSFlorian Fainelli 	ret = bcm7xxx_28nm_config_init(phydev);
2154fd14e0bSFlorian Fainelli 	if (ret)
2164fd14e0bSFlorian Fainelli 		return ret;
2174fd14e0bSFlorian Fainelli 
2184fd14e0bSFlorian Fainelli 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
2194fd14e0bSFlorian Fainelli 	 * or "hub" compliant advertised mode, fix that. This does not
2204fd14e0bSFlorian Fainelli 	 * cause any problems with the PHY library since genphy_config_aneg()
2214fd14e0bSFlorian Fainelli 	 * gracefully handles auto-negotiated and forced modes.
2224fd14e0bSFlorian Fainelli 	 */
2234fd14e0bSFlorian Fainelli 	return genphy_config_aneg(phydev);
2244fd14e0bSFlorian Fainelli }
2254fd14e0bSFlorian Fainelli 
226b560a58cSFlorian Fainelli static int phy_set_clr_bits(struct phy_device *dev, int location,
227b560a58cSFlorian Fainelli 					int set_mask, int clr_mask)
228b560a58cSFlorian Fainelli {
229b560a58cSFlorian Fainelli 	int v, ret;
230b560a58cSFlorian Fainelli 
231b560a58cSFlorian Fainelli 	v = phy_read(dev, location);
232b560a58cSFlorian Fainelli 	if (v < 0)
233b560a58cSFlorian Fainelli 		return v;
234b560a58cSFlorian Fainelli 
235b560a58cSFlorian Fainelli 	v &= ~clr_mask;
236b560a58cSFlorian Fainelli 	v |= set_mask;
237b560a58cSFlorian Fainelli 
238b560a58cSFlorian Fainelli 	ret = phy_write(dev, location, v);
239b560a58cSFlorian Fainelli 	if (ret < 0)
240b560a58cSFlorian Fainelli 		return ret;
241b560a58cSFlorian Fainelli 
242b560a58cSFlorian Fainelli 	return v;
243b560a58cSFlorian Fainelli }
244b560a58cSFlorian Fainelli 
245b560a58cSFlorian Fainelli static int bcm7xxx_config_init(struct phy_device *phydev)
246b560a58cSFlorian Fainelli {
247b560a58cSFlorian Fainelli 	int ret;
248b560a58cSFlorian Fainelli 
249b560a58cSFlorian Fainelli 	/* Enable 64 clock MDIO */
250b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
251b560a58cSFlorian Fainelli 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
252b560a58cSFlorian Fainelli 
253e18556eeSFlorian Fainelli 	/* Workaround only required for 100Mbits/sec capable PHYs */
254e18556eeSFlorian Fainelli 	if (phydev->supported & PHY_GBIT_FEATURES)
255b560a58cSFlorian Fainelli 		return 0;
256b560a58cSFlorian Fainelli 
257b560a58cSFlorian Fainelli 	/* set shadow mode 2 */
258b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
259b560a58cSFlorian Fainelli 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
260b560a58cSFlorian Fainelli 	if (ret < 0)
261b560a58cSFlorian Fainelli 		return ret;
262b560a58cSFlorian Fainelli 
263b560a58cSFlorian Fainelli 	/* set iddq_clkbias */
264b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
265b560a58cSFlorian Fainelli 	udelay(10);
266b560a58cSFlorian Fainelli 
267b560a58cSFlorian Fainelli 	/* reset iddq_clkbias */
268b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
269b560a58cSFlorian Fainelli 
270b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
271b560a58cSFlorian Fainelli 
272b560a58cSFlorian Fainelli 	/* reset shadow mode 2 */
273b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
274b560a58cSFlorian Fainelli 	if (ret < 0)
275b560a58cSFlorian Fainelli 		return ret;
276b560a58cSFlorian Fainelli 
277b560a58cSFlorian Fainelli 	return 0;
278b560a58cSFlorian Fainelli }
279b560a58cSFlorian Fainelli 
280b560a58cSFlorian Fainelli /* Workaround for putting the PHY in IDDQ mode, required
28182c084f5SFlorian Fainelli  * for all BCM7XXX 40nm and 65nm PHYs
282b560a58cSFlorian Fainelli  */
283b560a58cSFlorian Fainelli static int bcm7xxx_suspend(struct phy_device *phydev)
284b560a58cSFlorian Fainelli {
285b560a58cSFlorian Fainelli 	int ret;
286b560a58cSFlorian Fainelli 	const struct bcm7xxx_regs {
287b560a58cSFlorian Fainelli 		int reg;
288b560a58cSFlorian Fainelli 		u16 value;
289b560a58cSFlorian Fainelli 	} bcm7xxx_suspend_cfg[] = {
290b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x008b },
291b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
292b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
293b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000f },
294b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
295b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000b },
296b560a58cSFlorian Fainelli 	};
297b560a58cSFlorian Fainelli 	unsigned int i;
298b560a58cSFlorian Fainelli 
299b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
300b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
301b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].reg,
302b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].value);
303b560a58cSFlorian Fainelli 		if (ret)
304b560a58cSFlorian Fainelli 			return ret;
305b560a58cSFlorian Fainelli 	}
306b560a58cSFlorian Fainelli 
307b560a58cSFlorian Fainelli 	return 0;
308b560a58cSFlorian Fainelli }
309b560a58cSFlorian Fainelli 
310b560a58cSFlorian Fainelli static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
311b560a58cSFlorian Fainelli {
312b560a58cSFlorian Fainelli 	return 0;
313b560a58cSFlorian Fainelli }
314b560a58cSFlorian Fainelli 
315153df3c7SFlorian Fainelli #define BCM7XXX_28NM_GPHY(_oui, _name)					\
316153df3c7SFlorian Fainelli {									\
317153df3c7SFlorian Fainelli 	.phy_id		= (_oui),					\
318153df3c7SFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,					\
319153df3c7SFlorian Fainelli 	.name		= _name,					\
320153df3c7SFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |				\
321153df3c7SFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,	\
322153df3c7SFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,				\
3232a9df742SFlorian Fainelli 	.config_init	= bcm7xxx_28nm_config_init,			\
324153df3c7SFlorian Fainelli 	.config_aneg	= genphy_config_aneg,				\
325153df3c7SFlorian Fainelli 	.read_status	= genphy_read_status,				\
326153df3c7SFlorian Fainelli 	.resume		= bcm7xxx_28nm_resume,				\
327153df3c7SFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },			\
328153df3c7SFlorian Fainelli }
329153df3c7SFlorian Fainelli 
330b560a58cSFlorian Fainelli static struct phy_driver bcm7xxx_driver[] = {
331430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
332430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
333153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
334153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
33559e33c2bSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
336153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
337b560a58cSFlorian Fainelli {
338d068b02cSPetri Gynther 	.phy_id         = PHY_ID_BCM7425,
339d068b02cSPetri Gynther 	.phy_id_mask    = 0xfffffff0,
340d068b02cSPetri Gynther 	.name           = "Broadcom BCM7425",
341d068b02cSPetri Gynther 	.features       = PHY_GBIT_FEATURES |
342d068b02cSPetri Gynther 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
343cc4a84c3SFlorian Fainelli 	.flags          = PHY_IS_INTERNAL,
344d068b02cSPetri Gynther 	.config_init    = bcm7xxx_config_init,
345d068b02cSPetri Gynther 	.config_aneg    = genphy_config_aneg,
346d068b02cSPetri Gynther 	.read_status    = genphy_read_status,
347d068b02cSPetri Gynther 	.suspend        = bcm7xxx_suspend,
348d068b02cSPetri Gynther 	.resume         = bcm7xxx_config_init,
349d068b02cSPetri Gynther 	.driver         = { .owner = THIS_MODULE },
350d068b02cSPetri Gynther }, {
351d068b02cSPetri Gynther 	.phy_id         = PHY_ID_BCM7429,
352d068b02cSPetri Gynther 	.phy_id_mask    = 0xfffffff0,
353d068b02cSPetri Gynther 	.name           = "Broadcom BCM7429",
354d068b02cSPetri Gynther 	.features       = PHY_GBIT_FEATURES |
355d068b02cSPetri Gynther 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
356d068b02cSPetri Gynther 	.flags          = PHY_IS_INTERNAL,
357d068b02cSPetri Gynther 	.config_init    = bcm7xxx_config_init,
358d068b02cSPetri Gynther 	.config_aneg    = genphy_config_aneg,
359d068b02cSPetri Gynther 	.read_status    = genphy_read_status,
360d068b02cSPetri Gynther 	.suspend        = bcm7xxx_suspend,
361d068b02cSPetri Gynther 	.resume         = bcm7xxx_config_init,
362d068b02cSPetri Gynther 	.driver         = { .owner = THIS_MODULE },
363d068b02cSPetri Gynther }, {
3649458ceabSFlorian Fainelli 	.phy_id         = PHY_ID_BCM7435,
3659458ceabSFlorian Fainelli 	.phy_id_mask    = 0xfffffff0,
3669458ceabSFlorian Fainelli 	.name           = "Broadcom BCM7435",
3679458ceabSFlorian Fainelli 	.features       = PHY_GBIT_FEATURES |
3689458ceabSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
3699458ceabSFlorian Fainelli 	.flags          = PHY_IS_INTERNAL,
3709458ceabSFlorian Fainelli 	.config_init    = bcm7xxx_config_init,
3719458ceabSFlorian Fainelli 	.config_aneg    = genphy_config_aneg,
3729458ceabSFlorian Fainelli 	.read_status    = genphy_read_status,
3739458ceabSFlorian Fainelli 	.suspend        = bcm7xxx_suspend,
3749458ceabSFlorian Fainelli 	.resume         = bcm7xxx_config_init,
3759458ceabSFlorian Fainelli 	.driver         = { .owner = THIS_MODULE },
3769458ceabSFlorian Fainelli }, {
377b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_4,
378b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffff0000,
379b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 40nm",
380b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
381b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
382b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
383b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_config_init,
384b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
385b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
386b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
387b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
388b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
389b560a58cSFlorian Fainelli }, {
390b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_5,
391b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffffff00,
392b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 65nm",
393b560a58cSFlorian Fainelli 	.features	= PHY_BASIC_FEATURES |
394b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
395b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
396b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_dummy_config_init,
397b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
398b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
399b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
400b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
401b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
402b560a58cSFlorian Fainelli } };
403b560a58cSFlorian Fainelli 
404b560a58cSFlorian Fainelli static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
405430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7250, 0xfffffff0, },
406430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7364, 0xfffffff0, },
407b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7366, 0xfffffff0, },
408d068b02cSPetri Gynther 	{ PHY_ID_BCM7425, 0xfffffff0, },
409d068b02cSPetri Gynther 	{ PHY_ID_BCM7429, 0xfffffff0, },
410b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7439, 0xfffffff0, },
4119458ceabSFlorian Fainelli 	{ PHY_ID_BCM7435, 0xfffffff0, },
412b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7445, 0xfffffff0, },
413b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_4, 0xffff0000 },
414b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_5, 0xffffff00 },
415b560a58cSFlorian Fainelli 	{ }
416b560a58cSFlorian Fainelli };
417b560a58cSFlorian Fainelli 
41850fd7150SJohan Hovold module_phy_driver(bcm7xxx_driver);
419b560a58cSFlorian Fainelli 
420b560a58cSFlorian Fainelli MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
421b560a58cSFlorian Fainelli 
422b560a58cSFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
423b560a58cSFlorian Fainelli MODULE_LICENSE("GPL");
424b560a58cSFlorian Fainelli MODULE_AUTHOR("Broadcom Corporation");
425