xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision 4fd14e0b)
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>
15b560a58cSFlorian Fainelli #include <linux/bitops.h>
16b560a58cSFlorian Fainelli #include <linux/brcmphy.h>
17b560a58cSFlorian Fainelli 
18b560a58cSFlorian Fainelli /* Broadcom BCM7xxx internal PHY registers */
19b560a58cSFlorian Fainelli #define MII_BCM7XXX_CHANNEL_WIDTH	0x2000
20b560a58cSFlorian Fainelli 
21b560a58cSFlorian Fainelli /* 40nm only register definitions */
22b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_AUX_CTL	0x10
23b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_FALSE_CAR	0x13
24b560a58cSFlorian Fainelli #define MII_BCM7XXX_100TX_DISC		0x14
25b560a58cSFlorian Fainelli #define MII_BCM7XXX_AUX_MODE		0x1d
26b560a58cSFlorian Fainelli #define  MII_BCM7XX_64CLK_MDIO		BIT(12)
27b560a58cSFlorian Fainelli #define MII_BCM7XXX_CORE_BASE1E		0x1e
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)
41a3622f2cSFlorian Fainelli #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
42a3622f2cSFlorian Fainelli #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
43a3622f2cSFlorian Fainelli #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
44a3622f2cSFlorian Fainelli 
45a3622f2cSFlorian Fainelli #define CORE_EXPB0			0xb0
46a3622f2cSFlorian Fainelli 
47b560a58cSFlorian Fainelli static int bcm7445_config_init(struct phy_device *phydev)
48b560a58cSFlorian Fainelli {
49b560a58cSFlorian Fainelli 	int ret;
50b560a58cSFlorian Fainelli 	const struct bcm7445_regs {
51b560a58cSFlorian Fainelli 		int reg;
52b560a58cSFlorian Fainelli 		u16 value;
53b560a58cSFlorian Fainelli 	} bcm7445_regs_cfg[] = {
54b560a58cSFlorian Fainelli 		/* increases ADC latency by 24ns */
55b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x0038 },
56b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xAB95 },
57b560a58cSFlorian Fainelli 		/* increases internal 1V LDO voltage by 5% */
58b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x2038 },
59b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xBB22 },
60b560a58cSFlorian Fainelli 		/* reduce RX low pass filter corner frequency */
61b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x6038 },
62b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xFFC5 },
63b560a58cSFlorian Fainelli 		/* reduce RX high pass filter corner frequency */
64b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x003a },
65b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0x2002 },
66b560a58cSFlorian Fainelli 	};
67b560a58cSFlorian Fainelli 	unsigned int i;
68b560a58cSFlorian Fainelli 
69b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
70b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
71b560a58cSFlorian Fainelli 				bcm7445_regs_cfg[i].reg,
72b560a58cSFlorian Fainelli 				bcm7445_regs_cfg[i].value);
73b560a58cSFlorian Fainelli 		if (ret)
74b560a58cSFlorian Fainelli 			return ret;
75b560a58cSFlorian Fainelli 	}
76b560a58cSFlorian Fainelli 
77b560a58cSFlorian Fainelli 	return 0;
78b560a58cSFlorian Fainelli }
79b560a58cSFlorian Fainelli 
80b560a58cSFlorian Fainelli static void phy_write_exp(struct phy_device *phydev,
81b560a58cSFlorian Fainelli 					u16 reg, u16 value)
82b560a58cSFlorian Fainelli {
83b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
84b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
85b560a58cSFlorian Fainelli }
86b560a58cSFlorian Fainelli 
87b560a58cSFlorian Fainelli static void phy_write_misc(struct phy_device *phydev,
88b560a58cSFlorian Fainelli 					u16 reg, u16 chl, u16 value)
89b560a58cSFlorian Fainelli {
90b560a58cSFlorian Fainelli 	int tmp;
91b560a58cSFlorian Fainelli 
92b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
93b560a58cSFlorian Fainelli 
94b560a58cSFlorian Fainelli 	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
95b560a58cSFlorian Fainelli 	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
96b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
97b560a58cSFlorian Fainelli 
98b560a58cSFlorian Fainelli 	tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
99b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
100b560a58cSFlorian Fainelli 
101b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
102b560a58cSFlorian Fainelli }
103b560a58cSFlorian Fainelli 
104b560a58cSFlorian Fainelli static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
105b560a58cSFlorian Fainelli {
106b560a58cSFlorian Fainelli 	/* Increase VCO range to prevent unlocking problem of PLL at low
107b560a58cSFlorian Fainelli 	 * temp
108b560a58cSFlorian Fainelli 	 */
109a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
110b560a58cSFlorian Fainelli 
111b560a58cSFlorian Fainelli 	/* Change Ki to 011 */
112a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
113b560a58cSFlorian Fainelli 
114b560a58cSFlorian Fainelli 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
115b560a58cSFlorian Fainelli 	 * to 111
116b560a58cSFlorian Fainelli 	 */
117a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
118b560a58cSFlorian Fainelli 
119b560a58cSFlorian Fainelli 	/* Adjust bias current trim by -3 */
120a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, DSP_TAP10, 0x690b);
121b560a58cSFlorian Fainelli 
122b560a58cSFlorian Fainelli 	/* Switch to CORE_BASE1E */
123b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
124b560a58cSFlorian Fainelli 
125b560a58cSFlorian Fainelli 	/* Reset R_CAL/RC_CAL Engine */
126a3622f2cSFlorian Fainelli 	phy_write_exp(phydev, CORE_EXPB0, 0x0010);
127b560a58cSFlorian Fainelli 
128b560a58cSFlorian Fainelli 	/* Disable Reset R_CAL/RC_CAL Engine */
129a3622f2cSFlorian Fainelli 	phy_write_exp(phydev, CORE_EXPB0, 0x0000);
130b560a58cSFlorian Fainelli 
1319918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_0 */
1329918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
1339918542eSFlorian Fainelli 
1349918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_1 */
1359918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
1369918542eSFlorian Fainelli 
1379918542eSFlorian Fainelli 	/* write AFE_RX_LP_COUNTER */
138a62ea5a7SFlorian Fainelli 	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
1399918542eSFlorian Fainelli 
1409918542eSFlorian Fainelli 	/* write AFE_HPF_TRIM_OTHERS */
1419918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
1429918542eSFlorian Fainelli 
1439918542eSFlorian Fainelli 	/* write AFTE_TX_CONFIG */
1449918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
1459918542eSFlorian Fainelli 
146b560a58cSFlorian Fainelli 	return 0;
147b560a58cSFlorian Fainelli }
148b560a58cSFlorian Fainelli 
149b560a58cSFlorian Fainelli static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
150b560a58cSFlorian Fainelli {
151b560a58cSFlorian Fainelli 	int ret;
152b560a58cSFlorian Fainelli 
153b560a58cSFlorian Fainelli 	ret = bcm7445_config_init(phydev);
154b560a58cSFlorian Fainelli 	if (ret)
155b560a58cSFlorian Fainelli 		return ret;
156b560a58cSFlorian Fainelli 
157b560a58cSFlorian Fainelli 	return bcm7xxx_28nm_afe_config_init(phydev);
158b560a58cSFlorian Fainelli }
159b560a58cSFlorian Fainelli 
1604fd14e0bSFlorian Fainelli static int bcm7xxx_28nm_resume(struct phy_device *phydev)
1614fd14e0bSFlorian Fainelli {
1624fd14e0bSFlorian Fainelli 	int ret;
1634fd14e0bSFlorian Fainelli 
1644fd14e0bSFlorian Fainelli 	/* Re-apply workarounds coming out suspend/resume */
1654fd14e0bSFlorian Fainelli 	ret = bcm7xxx_28nm_config_init(phydev);
1664fd14e0bSFlorian Fainelli 	if (ret)
1674fd14e0bSFlorian Fainelli 		return ret;
1684fd14e0bSFlorian Fainelli 
1694fd14e0bSFlorian Fainelli 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
1704fd14e0bSFlorian Fainelli 	 * or "hub" compliant advertised mode, fix that. This does not
1714fd14e0bSFlorian Fainelli 	 * cause any problems with the PHY library since genphy_config_aneg()
1724fd14e0bSFlorian Fainelli 	 * gracefully handles auto-negotiated and forced modes.
1734fd14e0bSFlorian Fainelli 	 */
1744fd14e0bSFlorian Fainelli 	return genphy_config_aneg(phydev);
1754fd14e0bSFlorian Fainelli }
1764fd14e0bSFlorian Fainelli 
177b560a58cSFlorian Fainelli static int phy_set_clr_bits(struct phy_device *dev, int location,
178b560a58cSFlorian Fainelli 					int set_mask, int clr_mask)
179b560a58cSFlorian Fainelli {
180b560a58cSFlorian Fainelli 	int v, ret;
181b560a58cSFlorian Fainelli 
182b560a58cSFlorian Fainelli 	v = phy_read(dev, location);
183b560a58cSFlorian Fainelli 	if (v < 0)
184b560a58cSFlorian Fainelli 		return v;
185b560a58cSFlorian Fainelli 
186b560a58cSFlorian Fainelli 	v &= ~clr_mask;
187b560a58cSFlorian Fainelli 	v |= set_mask;
188b560a58cSFlorian Fainelli 
189b560a58cSFlorian Fainelli 	ret = phy_write(dev, location, v);
190b560a58cSFlorian Fainelli 	if (ret < 0)
191b560a58cSFlorian Fainelli 		return ret;
192b560a58cSFlorian Fainelli 
193b560a58cSFlorian Fainelli 	return v;
194b560a58cSFlorian Fainelli }
195b560a58cSFlorian Fainelli 
196b560a58cSFlorian Fainelli static int bcm7xxx_config_init(struct phy_device *phydev)
197b560a58cSFlorian Fainelli {
198b560a58cSFlorian Fainelli 	int ret;
199b560a58cSFlorian Fainelli 
200b560a58cSFlorian Fainelli 	/* Enable 64 clock MDIO */
201b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
202b560a58cSFlorian Fainelli 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
203b560a58cSFlorian Fainelli 
204b560a58cSFlorian Fainelli 	/* Workaround only required for 100Mbits/sec */
205b560a58cSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
206b560a58cSFlorian Fainelli 		return 0;
207b560a58cSFlorian Fainelli 
208b560a58cSFlorian Fainelli 	/* set shadow mode 2 */
209b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
210b560a58cSFlorian Fainelli 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
211b560a58cSFlorian Fainelli 	if (ret < 0)
212b560a58cSFlorian Fainelli 		return ret;
213b560a58cSFlorian Fainelli 
214b560a58cSFlorian Fainelli 	/* set iddq_clkbias */
215b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
216b560a58cSFlorian Fainelli 	udelay(10);
217b560a58cSFlorian Fainelli 
218b560a58cSFlorian Fainelli 	/* reset iddq_clkbias */
219b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
220b560a58cSFlorian Fainelli 
221b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
222b560a58cSFlorian Fainelli 
223b560a58cSFlorian Fainelli 	/* reset shadow mode 2 */
224b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
225b560a58cSFlorian Fainelli 	if (ret < 0)
226b560a58cSFlorian Fainelli 		return ret;
227b560a58cSFlorian Fainelli 
228b560a58cSFlorian Fainelli 	return 0;
229b560a58cSFlorian Fainelli }
230b560a58cSFlorian Fainelli 
231b560a58cSFlorian Fainelli /* Workaround for putting the PHY in IDDQ mode, required
232b560a58cSFlorian Fainelli  * for all BCM7XXX PHYs
233b560a58cSFlorian Fainelli  */
234b560a58cSFlorian Fainelli static int bcm7xxx_suspend(struct phy_device *phydev)
235b560a58cSFlorian Fainelli {
236b560a58cSFlorian Fainelli 	int ret;
237b560a58cSFlorian Fainelli 	const struct bcm7xxx_regs {
238b560a58cSFlorian Fainelli 		int reg;
239b560a58cSFlorian Fainelli 		u16 value;
240b560a58cSFlorian Fainelli 	} bcm7xxx_suspend_cfg[] = {
241b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x008b },
242b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
243b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
244b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000f },
245b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
246b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000b },
247b560a58cSFlorian Fainelli 	};
248b560a58cSFlorian Fainelli 	unsigned int i;
249b560a58cSFlorian Fainelli 
250b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
251b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
252b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].reg,
253b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].value);
254b560a58cSFlorian Fainelli 		if (ret)
255b560a58cSFlorian Fainelli 			return ret;
256b560a58cSFlorian Fainelli 	}
257b560a58cSFlorian Fainelli 
258b560a58cSFlorian Fainelli 	return 0;
259b560a58cSFlorian Fainelli }
260b560a58cSFlorian Fainelli 
261b560a58cSFlorian Fainelli static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
262b560a58cSFlorian Fainelli {
263b560a58cSFlorian Fainelli 	return 0;
264b560a58cSFlorian Fainelli }
265b560a58cSFlorian Fainelli 
266b560a58cSFlorian Fainelli static struct phy_driver bcm7xxx_driver[] = {
267b560a58cSFlorian Fainelli {
268b560a58cSFlorian Fainelli 	.phy_id		= PHY_ID_BCM7366,
269b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
270b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7366",
271b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
272b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
273b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
274b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_28nm_afe_config_init,
275b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
276b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
277b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
2784fd14e0bSFlorian Fainelli 	.resume		= bcm7xxx_28nm_resume,
279b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
280b560a58cSFlorian Fainelli }, {
281b560a58cSFlorian Fainelli 	.phy_id		= PHY_ID_BCM7439,
282b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
283b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7439",
284b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
285b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
286b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
287b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_28nm_afe_config_init,
288b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
289b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
290b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
2914fd14e0bSFlorian Fainelli 	.resume		= bcm7xxx_28nm_resume,
292b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
293b560a58cSFlorian Fainelli }, {
294b560a58cSFlorian Fainelli 	.phy_id		= PHY_ID_BCM7445,
295b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,
296b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7445",
297b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
298b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
299b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
300b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_28nm_config_init,
301b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
302b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
303b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
3044fd14e0bSFlorian Fainelli 	.resume		= bcm7xxx_28nm_afe_config_init,
305b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
306b560a58cSFlorian Fainelli }, {
307b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_4,
308b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffff0000,
309b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 40nm",
310b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
311b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
312b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
313b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_config_init,
314b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
315b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
316b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
317b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
318b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
319b560a58cSFlorian Fainelli }, {
320b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_5,
321b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffffff00,
322b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 65nm",
323b560a58cSFlorian Fainelli 	.features	= PHY_BASIC_FEATURES |
324b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
325b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
326b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_dummy_config_init,
327b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
328b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
329b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
330b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
331b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
332b560a58cSFlorian Fainelli } };
333b560a58cSFlorian Fainelli 
334b560a58cSFlorian Fainelli static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
335b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7366, 0xfffffff0, },
336b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7439, 0xfffffff0, },
337b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7445, 0xfffffff0, },
338b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_4, 0xffff0000 },
339b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_5, 0xffffff00 },
340b560a58cSFlorian Fainelli 	{ }
341b560a58cSFlorian Fainelli };
342b560a58cSFlorian Fainelli 
343b560a58cSFlorian Fainelli static int __init bcm7xxx_phy_init(void)
344b560a58cSFlorian Fainelli {
345b560a58cSFlorian Fainelli 	return phy_drivers_register(bcm7xxx_driver,
346b560a58cSFlorian Fainelli 			ARRAY_SIZE(bcm7xxx_driver));
347b560a58cSFlorian Fainelli }
348b560a58cSFlorian Fainelli 
349b560a58cSFlorian Fainelli static void __exit bcm7xxx_phy_exit(void)
350b560a58cSFlorian Fainelli {
351b560a58cSFlorian Fainelli 	phy_drivers_unregister(bcm7xxx_driver,
352b560a58cSFlorian Fainelli 			ARRAY_SIZE(bcm7xxx_driver));
353b560a58cSFlorian Fainelli }
354b560a58cSFlorian Fainelli 
355b560a58cSFlorian Fainelli module_init(bcm7xxx_phy_init);
356b560a58cSFlorian Fainelli module_exit(bcm7xxx_phy_exit);
357b560a58cSFlorian Fainelli 
358b560a58cSFlorian Fainelli MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
359b560a58cSFlorian Fainelli 
360b560a58cSFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
361b560a58cSFlorian Fainelli MODULE_LICENSE("GPL");
362b560a58cSFlorian Fainelli MODULE_AUTHOR("Broadcom Corporation");
363