xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision 430ad68f)
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>
17b8f9a029SFlorian Fainelli #include <linux/mdio.h>
18b560a58cSFlorian Fainelli 
19b560a58cSFlorian Fainelli /* Broadcom BCM7xxx internal PHY registers */
20b560a58cSFlorian Fainelli #define MII_BCM7XXX_CHANNEL_WIDTH	0x2000
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_CORE_BASE1E		0x1e
29b560a58cSFlorian Fainelli #define MII_BCM7XXX_TEST		0x1f
30b560a58cSFlorian Fainelli #define  MII_BCM7XXX_SHD_MODE_2		BIT(2)
31b560a58cSFlorian Fainelli 
32a3622f2cSFlorian Fainelli /* 28nm only register definitions */
33a3622f2cSFlorian Fainelli #define MISC_ADDR(base, channel)	base, channel
34a3622f2cSFlorian Fainelli 
35a3622f2cSFlorian Fainelli #define DSP_TAP10			MISC_ADDR(0x0a, 0)
36a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_1			MISC_ADDR(0x32, 1)
37a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_2			MISC_ADDR(0x32, 2)
38a3622f2cSFlorian Fainelli #define PLL_PLLCTRL_4			MISC_ADDR(0x33, 0)
39a3622f2cSFlorian Fainelli 
40a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_0			MISC_ADDR(0x38, 0)
41a3622f2cSFlorian Fainelli #define AFE_RXCONFIG_1			MISC_ADDR(0x38, 1)
42a3622f2cSFlorian Fainelli #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
43a3622f2cSFlorian Fainelli #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
44a3622f2cSFlorian Fainelli #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
45a3622f2cSFlorian Fainelli 
46a3622f2cSFlorian Fainelli #define CORE_EXPB0			0xb0
47a3622f2cSFlorian Fainelli 
48b560a58cSFlorian Fainelli static int bcm7445_config_init(struct phy_device *phydev)
49b560a58cSFlorian Fainelli {
50b560a58cSFlorian Fainelli 	int ret;
51b560a58cSFlorian Fainelli 	const struct bcm7445_regs {
52b560a58cSFlorian Fainelli 		int reg;
53b560a58cSFlorian Fainelli 		u16 value;
54b560a58cSFlorian Fainelli 	} bcm7445_regs_cfg[] = {
55b560a58cSFlorian Fainelli 		/* increases ADC latency by 24ns */
56b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x0038 },
57b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xAB95 },
58b560a58cSFlorian Fainelli 		/* increases internal 1V LDO voltage by 5% */
59b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x2038 },
60b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xBB22 },
61b560a58cSFlorian Fainelli 		/* reduce RX low pass filter corner frequency */
62b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x6038 },
63b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0xFFC5 },
64b560a58cSFlorian Fainelli 		/* reduce RX high pass filter corner frequency */
65b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_SEL, 0x003a },
66b560a58cSFlorian Fainelli 		{ MII_BCM54XX_EXP_DATA, 0x2002 },
67b560a58cSFlorian Fainelli 	};
68b560a58cSFlorian Fainelli 	unsigned int i;
69b560a58cSFlorian Fainelli 
70b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
71b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
72b560a58cSFlorian Fainelli 				bcm7445_regs_cfg[i].reg,
73b560a58cSFlorian Fainelli 				bcm7445_regs_cfg[i].value);
74b560a58cSFlorian Fainelli 		if (ret)
75b560a58cSFlorian Fainelli 			return ret;
76b560a58cSFlorian Fainelli 	}
77b560a58cSFlorian Fainelli 
78b560a58cSFlorian Fainelli 	return 0;
79b560a58cSFlorian Fainelli }
80b560a58cSFlorian Fainelli 
81b560a58cSFlorian Fainelli static void phy_write_exp(struct phy_device *phydev,
82b560a58cSFlorian Fainelli 					u16 reg, u16 value)
83b560a58cSFlorian Fainelli {
84b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
85b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
86b560a58cSFlorian Fainelli }
87b560a58cSFlorian Fainelli 
88b560a58cSFlorian Fainelli static void phy_write_misc(struct phy_device *phydev,
89b560a58cSFlorian Fainelli 					u16 reg, u16 chl, u16 value)
90b560a58cSFlorian Fainelli {
91b560a58cSFlorian Fainelli 	int tmp;
92b560a58cSFlorian Fainelli 
93b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
94b560a58cSFlorian Fainelli 
95b560a58cSFlorian Fainelli 	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
96b560a58cSFlorian Fainelli 	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
97b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
98b560a58cSFlorian Fainelli 
99b560a58cSFlorian Fainelli 	tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
100b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
101b560a58cSFlorian Fainelli 
102b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
103b560a58cSFlorian Fainelli }
104b560a58cSFlorian Fainelli 
105b560a58cSFlorian Fainelli static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
106b560a58cSFlorian Fainelli {
107b560a58cSFlorian Fainelli 	/* Increase VCO range to prevent unlocking problem of PLL at low
108b560a58cSFlorian Fainelli 	 * temp
109b560a58cSFlorian Fainelli 	 */
110a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
111b560a58cSFlorian Fainelli 
112b560a58cSFlorian Fainelli 	/* Change Ki to 011 */
113a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
114b560a58cSFlorian Fainelli 
115b560a58cSFlorian Fainelli 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
116b560a58cSFlorian Fainelli 	 * to 111
117b560a58cSFlorian Fainelli 	 */
118a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
119b560a58cSFlorian Fainelli 
120b560a58cSFlorian Fainelli 	/* Adjust bias current trim by -3 */
121a3622f2cSFlorian Fainelli 	phy_write_misc(phydev, DSP_TAP10, 0x690b);
122b560a58cSFlorian Fainelli 
123b560a58cSFlorian Fainelli 	/* Switch to CORE_BASE1E */
124b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
125b560a58cSFlorian Fainelli 
126b560a58cSFlorian Fainelli 	/* Reset R_CAL/RC_CAL Engine */
127a3622f2cSFlorian Fainelli 	phy_write_exp(phydev, CORE_EXPB0, 0x0010);
128b560a58cSFlorian Fainelli 
129b560a58cSFlorian Fainelli 	/* Disable Reset R_CAL/RC_CAL Engine */
130a3622f2cSFlorian Fainelli 	phy_write_exp(phydev, CORE_EXPB0, 0x0000);
131b560a58cSFlorian Fainelli 
1329918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_0 */
1339918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
1349918542eSFlorian Fainelli 
1359918542eSFlorian Fainelli 	/* write AFE_RXCONFIG_1 */
1369918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
1379918542eSFlorian Fainelli 
1389918542eSFlorian Fainelli 	/* write AFE_RX_LP_COUNTER */
139a62ea5a7SFlorian Fainelli 	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
1409918542eSFlorian Fainelli 
1419918542eSFlorian Fainelli 	/* write AFE_HPF_TRIM_OTHERS */
1429918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
1439918542eSFlorian Fainelli 
1449918542eSFlorian Fainelli 	/* write AFTE_TX_CONFIG */
1459918542eSFlorian Fainelli 	phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
1469918542eSFlorian Fainelli 
147b560a58cSFlorian Fainelli 	return 0;
148b560a58cSFlorian Fainelli }
149b560a58cSFlorian Fainelli 
1509df54ddaSFlorian Fainelli static int bcm7xxx_apd_enable(struct phy_device *phydev)
1519df54ddaSFlorian Fainelli {
1529df54ddaSFlorian Fainelli 	int val;
1539df54ddaSFlorian Fainelli 
1549df54ddaSFlorian Fainelli 	/* Enable powering down of the DLL during auto-power down */
1559df54ddaSFlorian Fainelli 	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
1569df54ddaSFlorian Fainelli 	if (val < 0)
1579df54ddaSFlorian Fainelli 		return val;
1589df54ddaSFlorian Fainelli 
1599df54ddaSFlorian Fainelli 	val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
1609df54ddaSFlorian Fainelli 	bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
1619df54ddaSFlorian Fainelli 
1629df54ddaSFlorian Fainelli 	/* Enable auto-power down */
1639df54ddaSFlorian Fainelli 	val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
1649df54ddaSFlorian Fainelli 	if (val < 0)
1659df54ddaSFlorian Fainelli 		return val;
1669df54ddaSFlorian Fainelli 
1679df54ddaSFlorian Fainelli 	val |= BCM54XX_SHD_APD_EN;
1689df54ddaSFlorian Fainelli 	return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
1699df54ddaSFlorian Fainelli }
1709df54ddaSFlorian Fainelli 
171b8f9a029SFlorian Fainelli static int bcm7xxx_eee_enable(struct phy_device *phydev)
172b8f9a029SFlorian Fainelli {
173b8f9a029SFlorian Fainelli 	int val;
174b8f9a029SFlorian Fainelli 
175b8f9a029SFlorian Fainelli 	val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
176b8f9a029SFlorian Fainelli 				    MDIO_MMD_AN, phydev->addr);
177b8f9a029SFlorian Fainelli 	if (val < 0)
178b8f9a029SFlorian Fainelli 		return val;
179b8f9a029SFlorian Fainelli 
180b8f9a029SFlorian Fainelli 	/* Enable general EEE feature at the PHY level */
181b8f9a029SFlorian Fainelli 	val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
182b8f9a029SFlorian Fainelli 
183b8f9a029SFlorian Fainelli 	phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
184b8f9a029SFlorian Fainelli 			       MDIO_MMD_AN, phydev->addr, val);
185b8f9a029SFlorian Fainelli 
186b8f9a029SFlorian Fainelli 	/* Advertise supported modes */
187b8f9a029SFlorian Fainelli 	val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
188b8f9a029SFlorian Fainelli 				    MDIO_MMD_AN, phydev->addr);
189b8f9a029SFlorian Fainelli 
190b8f9a029SFlorian Fainelli 	val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
191b8f9a029SFlorian Fainelli 	phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
192b8f9a029SFlorian Fainelli 			       MDIO_MMD_AN, phydev->addr, val);
193b8f9a029SFlorian Fainelli 
194b8f9a029SFlorian Fainelli 	return 0;
195b8f9a029SFlorian Fainelli }
196b8f9a029SFlorian Fainelli 
197b560a58cSFlorian Fainelli static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
198b560a58cSFlorian Fainelli {
199b560a58cSFlorian Fainelli 	int ret;
200b560a58cSFlorian Fainelli 
201b560a58cSFlorian Fainelli 	ret = bcm7445_config_init(phydev);
202b560a58cSFlorian Fainelli 	if (ret)
203b560a58cSFlorian Fainelli 		return ret;
204b560a58cSFlorian Fainelli 
2059df54ddaSFlorian Fainelli 	ret = bcm7xxx_28nm_afe_config_init(phydev);
2069df54ddaSFlorian Fainelli 	if (ret)
2079df54ddaSFlorian Fainelli 		return ret;
2089df54ddaSFlorian Fainelli 
209b8f9a029SFlorian Fainelli 	ret = bcm7xxx_eee_enable(phydev);
210b8f9a029SFlorian Fainelli 	if (ret)
211b8f9a029SFlorian Fainelli 		return ret;
212b8f9a029SFlorian Fainelli 
2139df54ddaSFlorian Fainelli 	return bcm7xxx_apd_enable(phydev);
214b560a58cSFlorian Fainelli }
215b560a58cSFlorian Fainelli 
2164fd14e0bSFlorian Fainelli static int bcm7xxx_28nm_resume(struct phy_device *phydev)
2174fd14e0bSFlorian Fainelli {
2184fd14e0bSFlorian Fainelli 	int ret;
2194fd14e0bSFlorian Fainelli 
2204fd14e0bSFlorian Fainelli 	/* Re-apply workarounds coming out suspend/resume */
2214fd14e0bSFlorian Fainelli 	ret = bcm7xxx_28nm_config_init(phydev);
2224fd14e0bSFlorian Fainelli 	if (ret)
2234fd14e0bSFlorian Fainelli 		return ret;
2244fd14e0bSFlorian Fainelli 
2254fd14e0bSFlorian Fainelli 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
2264fd14e0bSFlorian Fainelli 	 * or "hub" compliant advertised mode, fix that. This does not
2274fd14e0bSFlorian Fainelli 	 * cause any problems with the PHY library since genphy_config_aneg()
2284fd14e0bSFlorian Fainelli 	 * gracefully handles auto-negotiated and forced modes.
2294fd14e0bSFlorian Fainelli 	 */
2304fd14e0bSFlorian Fainelli 	return genphy_config_aneg(phydev);
2314fd14e0bSFlorian Fainelli }
2324fd14e0bSFlorian Fainelli 
233b560a58cSFlorian Fainelli static int phy_set_clr_bits(struct phy_device *dev, int location,
234b560a58cSFlorian Fainelli 					int set_mask, int clr_mask)
235b560a58cSFlorian Fainelli {
236b560a58cSFlorian Fainelli 	int v, ret;
237b560a58cSFlorian Fainelli 
238b560a58cSFlorian Fainelli 	v = phy_read(dev, location);
239b560a58cSFlorian Fainelli 	if (v < 0)
240b560a58cSFlorian Fainelli 		return v;
241b560a58cSFlorian Fainelli 
242b560a58cSFlorian Fainelli 	v &= ~clr_mask;
243b560a58cSFlorian Fainelli 	v |= set_mask;
244b560a58cSFlorian Fainelli 
245b560a58cSFlorian Fainelli 	ret = phy_write(dev, location, v);
246b560a58cSFlorian Fainelli 	if (ret < 0)
247b560a58cSFlorian Fainelli 		return ret;
248b560a58cSFlorian Fainelli 
249b560a58cSFlorian Fainelli 	return v;
250b560a58cSFlorian Fainelli }
251b560a58cSFlorian Fainelli 
252b560a58cSFlorian Fainelli static int bcm7xxx_config_init(struct phy_device *phydev)
253b560a58cSFlorian Fainelli {
254b560a58cSFlorian Fainelli 	int ret;
255b560a58cSFlorian Fainelli 
256b560a58cSFlorian Fainelli 	/* Enable 64 clock MDIO */
257b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
258b560a58cSFlorian Fainelli 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
259b560a58cSFlorian Fainelli 
260b560a58cSFlorian Fainelli 	/* Workaround only required for 100Mbits/sec */
261b560a58cSFlorian Fainelli 	if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
262b560a58cSFlorian Fainelli 		return 0;
263b560a58cSFlorian Fainelli 
264b560a58cSFlorian Fainelli 	/* set shadow mode 2 */
265b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
266b560a58cSFlorian Fainelli 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
267b560a58cSFlorian Fainelli 	if (ret < 0)
268b560a58cSFlorian Fainelli 		return ret;
269b560a58cSFlorian Fainelli 
270b560a58cSFlorian Fainelli 	/* set iddq_clkbias */
271b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
272b560a58cSFlorian Fainelli 	udelay(10);
273b560a58cSFlorian Fainelli 
274b560a58cSFlorian Fainelli 	/* reset iddq_clkbias */
275b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
276b560a58cSFlorian Fainelli 
277b560a58cSFlorian Fainelli 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
278b560a58cSFlorian Fainelli 
279b560a58cSFlorian Fainelli 	/* reset shadow mode 2 */
280b560a58cSFlorian Fainelli 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
281b560a58cSFlorian Fainelli 	if (ret < 0)
282b560a58cSFlorian Fainelli 		return ret;
283b560a58cSFlorian Fainelli 
284b560a58cSFlorian Fainelli 	return 0;
285b560a58cSFlorian Fainelli }
286b560a58cSFlorian Fainelli 
287b560a58cSFlorian Fainelli /* Workaround for putting the PHY in IDDQ mode, required
28882c084f5SFlorian Fainelli  * for all BCM7XXX 40nm and 65nm PHYs
289b560a58cSFlorian Fainelli  */
290b560a58cSFlorian Fainelli static int bcm7xxx_suspend(struct phy_device *phydev)
291b560a58cSFlorian Fainelli {
292b560a58cSFlorian Fainelli 	int ret;
293b560a58cSFlorian Fainelli 	const struct bcm7xxx_regs {
294b560a58cSFlorian Fainelli 		int reg;
295b560a58cSFlorian Fainelli 		u16 value;
296b560a58cSFlorian Fainelli 	} bcm7xxx_suspend_cfg[] = {
297b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x008b },
298b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
299b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
300b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000f },
301b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
302b560a58cSFlorian Fainelli 		{ MII_BCM7XXX_TEST, 0x000b },
303b560a58cSFlorian Fainelli 	};
304b560a58cSFlorian Fainelli 	unsigned int i;
305b560a58cSFlorian Fainelli 
306b560a58cSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
307b560a58cSFlorian Fainelli 		ret = phy_write(phydev,
308b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].reg,
309b560a58cSFlorian Fainelli 				bcm7xxx_suspend_cfg[i].value);
310b560a58cSFlorian Fainelli 		if (ret)
311b560a58cSFlorian Fainelli 			return ret;
312b560a58cSFlorian Fainelli 	}
313b560a58cSFlorian Fainelli 
314b560a58cSFlorian Fainelli 	return 0;
315b560a58cSFlorian Fainelli }
316b560a58cSFlorian Fainelli 
317b560a58cSFlorian Fainelli static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
318b560a58cSFlorian Fainelli {
319b560a58cSFlorian Fainelli 	return 0;
320b560a58cSFlorian Fainelli }
321b560a58cSFlorian Fainelli 
322153df3c7SFlorian Fainelli #define BCM7XXX_28NM_GPHY(_oui, _name)					\
323153df3c7SFlorian Fainelli {									\
324153df3c7SFlorian Fainelli 	.phy_id		= (_oui),					\
325153df3c7SFlorian Fainelli 	.phy_id_mask	= 0xfffffff0,					\
326153df3c7SFlorian Fainelli 	.name		= _name,					\
327153df3c7SFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |				\
328153df3c7SFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,	\
329153df3c7SFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,				\
330153df3c7SFlorian Fainelli 	.config_init	= bcm7xxx_28nm_afe_config_init,			\
331153df3c7SFlorian Fainelli 	.config_aneg	= genphy_config_aneg,				\
332153df3c7SFlorian Fainelli 	.read_status	= genphy_read_status,				\
333153df3c7SFlorian Fainelli 	.resume		= bcm7xxx_28nm_resume,				\
334153df3c7SFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },			\
335153df3c7SFlorian Fainelli }
336153df3c7SFlorian Fainelli 
337b560a58cSFlorian Fainelli static struct phy_driver bcm7xxx_driver[] = {
338430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
339430ad68fSFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
340153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
341153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
342153df3c7SFlorian Fainelli 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
343b560a58cSFlorian Fainelli {
344b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_4,
345b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffff0000,
346b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 40nm",
347b560a58cSFlorian Fainelli 	.features	= PHY_GBIT_FEATURES |
348b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
349b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
350b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_config_init,
351b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
352b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
353b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
354b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
355b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
356b560a58cSFlorian Fainelli }, {
357b560a58cSFlorian Fainelli 	.phy_id		= PHY_BCM_OUI_5,
358b560a58cSFlorian Fainelli 	.phy_id_mask	= 0xffffff00,
359b560a58cSFlorian Fainelli 	.name		= "Broadcom BCM7XXX 65nm",
360b560a58cSFlorian Fainelli 	.features	= PHY_BASIC_FEATURES |
361b560a58cSFlorian Fainelli 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
362b560a58cSFlorian Fainelli 	.flags		= PHY_IS_INTERNAL,
363b560a58cSFlorian Fainelli 	.config_init	= bcm7xxx_dummy_config_init,
364b560a58cSFlorian Fainelli 	.config_aneg	= genphy_config_aneg,
365b560a58cSFlorian Fainelli 	.read_status	= genphy_read_status,
366b560a58cSFlorian Fainelli 	.suspend	= bcm7xxx_suspend,
367b560a58cSFlorian Fainelli 	.resume		= bcm7xxx_config_init,
368b560a58cSFlorian Fainelli 	.driver		= { .owner = THIS_MODULE },
369b560a58cSFlorian Fainelli } };
370b560a58cSFlorian Fainelli 
371b560a58cSFlorian Fainelli static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
372430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7250, 0xfffffff0, },
373430ad68fSFlorian Fainelli 	{ PHY_ID_BCM7364, 0xfffffff0, },
374b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7366, 0xfffffff0, },
375b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7439, 0xfffffff0, },
376b560a58cSFlorian Fainelli 	{ PHY_ID_BCM7445, 0xfffffff0, },
377b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_4, 0xffff0000 },
378b560a58cSFlorian Fainelli 	{ PHY_BCM_OUI_5, 0xffffff00 },
379b560a58cSFlorian Fainelli 	{ }
380b560a58cSFlorian Fainelli };
381b560a58cSFlorian Fainelli 
382b560a58cSFlorian Fainelli static int __init bcm7xxx_phy_init(void)
383b560a58cSFlorian Fainelli {
384b560a58cSFlorian Fainelli 	return phy_drivers_register(bcm7xxx_driver,
385b560a58cSFlorian Fainelli 			ARRAY_SIZE(bcm7xxx_driver));
386b560a58cSFlorian Fainelli }
387b560a58cSFlorian Fainelli 
388b560a58cSFlorian Fainelli static void __exit bcm7xxx_phy_exit(void)
389b560a58cSFlorian Fainelli {
390b560a58cSFlorian Fainelli 	phy_drivers_unregister(bcm7xxx_driver,
391b560a58cSFlorian Fainelli 			ARRAY_SIZE(bcm7xxx_driver));
392b560a58cSFlorian Fainelli }
393b560a58cSFlorian Fainelli 
394b560a58cSFlorian Fainelli module_init(bcm7xxx_phy_init);
395b560a58cSFlorian Fainelli module_exit(bcm7xxx_phy_exit);
396b560a58cSFlorian Fainelli 
397b560a58cSFlorian Fainelli MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
398b560a58cSFlorian Fainelli 
399b560a58cSFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
400b560a58cSFlorian Fainelli MODULE_LICENSE("GPL");
401b560a58cSFlorian Fainelli MODULE_AUTHOR("Broadcom Corporation");
402