xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision 94c7b6fc)
1 /*
2  * Broadcom BCM7xxx internal transceivers support.
3  *
4  * Copyright (C) 2014, Broadcom Corporation
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/module.h>
13 #include <linux/phy.h>
14 #include <linux/delay.h>
15 #include <linux/bitops.h>
16 #include <linux/brcmphy.h>
17 
18 /* Broadcom BCM7xxx internal PHY registers */
19 #define MII_BCM7XXX_CHANNEL_WIDTH	0x2000
20 
21 /* 40nm only register definitions */
22 #define MII_BCM7XXX_100TX_AUX_CTL	0x10
23 #define MII_BCM7XXX_100TX_FALSE_CAR	0x13
24 #define MII_BCM7XXX_100TX_DISC		0x14
25 #define MII_BCM7XXX_AUX_MODE		0x1d
26 #define  MII_BCM7XX_64CLK_MDIO		BIT(12)
27 #define MII_BCM7XXX_CORE_BASE1E		0x1e
28 #define MII_BCM7XXX_TEST		0x1f
29 #define  MII_BCM7XXX_SHD_MODE_2		BIT(2)
30 
31 /* 28nm only register definitions */
32 #define MISC_ADDR(base, channel)	base, channel
33 
34 #define DSP_TAP10			MISC_ADDR(0x0a, 0)
35 #define PLL_PLLCTRL_1			MISC_ADDR(0x32, 1)
36 #define PLL_PLLCTRL_2			MISC_ADDR(0x32, 2)
37 #define PLL_PLLCTRL_4			MISC_ADDR(0x33, 0)
38 
39 #define AFE_RXCONFIG_0			MISC_ADDR(0x38, 0)
40 #define AFE_RXCONFIG_1			MISC_ADDR(0x38, 1)
41 #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
42 #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
43 #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
44 
45 #define CORE_EXPB0			0xb0
46 
47 static int bcm7445_config_init(struct phy_device *phydev)
48 {
49 	int ret;
50 	const struct bcm7445_regs {
51 		int reg;
52 		u16 value;
53 	} bcm7445_regs_cfg[] = {
54 		/* increases ADC latency by 24ns */
55 		{ MII_BCM54XX_EXP_SEL, 0x0038 },
56 		{ MII_BCM54XX_EXP_DATA, 0xAB95 },
57 		/* increases internal 1V LDO voltage by 5% */
58 		{ MII_BCM54XX_EXP_SEL, 0x2038 },
59 		{ MII_BCM54XX_EXP_DATA, 0xBB22 },
60 		/* reduce RX low pass filter corner frequency */
61 		{ MII_BCM54XX_EXP_SEL, 0x6038 },
62 		{ MII_BCM54XX_EXP_DATA, 0xFFC5 },
63 		/* reduce RX high pass filter corner frequency */
64 		{ MII_BCM54XX_EXP_SEL, 0x003a },
65 		{ MII_BCM54XX_EXP_DATA, 0x2002 },
66 	};
67 	unsigned int i;
68 
69 	for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
70 		ret = phy_write(phydev,
71 				bcm7445_regs_cfg[i].reg,
72 				bcm7445_regs_cfg[i].value);
73 		if (ret)
74 			return ret;
75 	}
76 
77 	return 0;
78 }
79 
80 static void phy_write_exp(struct phy_device *phydev,
81 					u16 reg, u16 value)
82 {
83 	phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
84 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
85 }
86 
87 static void phy_write_misc(struct phy_device *phydev,
88 					u16 reg, u16 chl, u16 value)
89 {
90 	int tmp;
91 
92 	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
93 
94 	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
95 	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
96 	phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
97 
98 	tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
99 	phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
100 
101 	phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
102 }
103 
104 static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
105 {
106 	/* Increase VCO range to prevent unlocking problem of PLL at low
107 	 * temp
108 	 */
109 	phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
110 
111 	/* Change Ki to 011 */
112 	phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
113 
114 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
115 	 * to 111
116 	 */
117 	phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
118 
119 	/* Adjust bias current trim by -3 */
120 	phy_write_misc(phydev, DSP_TAP10, 0x690b);
121 
122 	/* Switch to CORE_BASE1E */
123 	phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
124 
125 	/* Reset R_CAL/RC_CAL Engine */
126 	phy_write_exp(phydev, CORE_EXPB0, 0x0010);
127 
128 	/* Disable Reset R_CAL/RC_CAL Engine */
129 	phy_write_exp(phydev, CORE_EXPB0, 0x0000);
130 
131 	/* write AFE_RXCONFIG_0 */
132 	phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
133 
134 	/* write AFE_RXCONFIG_1 */
135 	phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
136 
137 	/* write AFE_RX_LP_COUNTER */
138 	phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
139 
140 	/* write AFE_HPF_TRIM_OTHERS */
141 	phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
142 
143 	/* write AFTE_TX_CONFIG */
144 	phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
145 
146 	return 0;
147 }
148 
149 static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
150 {
151 	int ret;
152 
153 	ret = bcm7445_config_init(phydev);
154 	if (ret)
155 		return ret;
156 
157 	return bcm7xxx_28nm_afe_config_init(phydev);
158 }
159 
160 static int phy_set_clr_bits(struct phy_device *dev, int location,
161 					int set_mask, int clr_mask)
162 {
163 	int v, ret;
164 
165 	v = phy_read(dev, location);
166 	if (v < 0)
167 		return v;
168 
169 	v &= ~clr_mask;
170 	v |= set_mask;
171 
172 	ret = phy_write(dev, location, v);
173 	if (ret < 0)
174 		return ret;
175 
176 	return v;
177 }
178 
179 static int bcm7xxx_config_init(struct phy_device *phydev)
180 {
181 	int ret;
182 
183 	/* Enable 64 clock MDIO */
184 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
185 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
186 
187 	/* Workaround only required for 100Mbits/sec */
188 	if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
189 		return 0;
190 
191 	/* set shadow mode 2 */
192 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
193 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
194 	if (ret < 0)
195 		return ret;
196 
197 	/* set iddq_clkbias */
198 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
199 	udelay(10);
200 
201 	/* reset iddq_clkbias */
202 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
203 
204 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
205 
206 	/* reset shadow mode 2 */
207 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
208 	if (ret < 0)
209 		return ret;
210 
211 	return 0;
212 }
213 
214 /* Workaround for putting the PHY in IDDQ mode, required
215  * for all BCM7XXX PHYs
216  */
217 static int bcm7xxx_suspend(struct phy_device *phydev)
218 {
219 	int ret;
220 	const struct bcm7xxx_regs {
221 		int reg;
222 		u16 value;
223 	} bcm7xxx_suspend_cfg[] = {
224 		{ MII_BCM7XXX_TEST, 0x008b },
225 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
226 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
227 		{ MII_BCM7XXX_TEST, 0x000f },
228 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
229 		{ MII_BCM7XXX_TEST, 0x000b },
230 	};
231 	unsigned int i;
232 
233 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
234 		ret = phy_write(phydev,
235 				bcm7xxx_suspend_cfg[i].reg,
236 				bcm7xxx_suspend_cfg[i].value);
237 		if (ret)
238 			return ret;
239 	}
240 
241 	return 0;
242 }
243 
244 static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
245 {
246 	return 0;
247 }
248 
249 static struct phy_driver bcm7xxx_driver[] = {
250 {
251 	.phy_id		= PHY_ID_BCM7366,
252 	.phy_id_mask	= 0xfffffff0,
253 	.name		= "Broadcom BCM7366",
254 	.features	= PHY_GBIT_FEATURES |
255 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
256 	.flags		= PHY_IS_INTERNAL,
257 	.config_init	= bcm7xxx_28nm_afe_config_init,
258 	.config_aneg	= genphy_config_aneg,
259 	.read_status	= genphy_read_status,
260 	.suspend	= bcm7xxx_suspend,
261 	.resume		= bcm7xxx_28nm_afe_config_init,
262 	.driver		= { .owner = THIS_MODULE },
263 }, {
264 	.phy_id		= PHY_ID_BCM7439,
265 	.phy_id_mask	= 0xfffffff0,
266 	.name		= "Broadcom BCM7439",
267 	.features	= PHY_GBIT_FEATURES |
268 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
269 	.flags		= PHY_IS_INTERNAL,
270 	.config_init	= bcm7xxx_28nm_afe_config_init,
271 	.config_aneg	= genphy_config_aneg,
272 	.read_status	= genphy_read_status,
273 	.suspend	= bcm7xxx_suspend,
274 	.resume		= bcm7xxx_28nm_afe_config_init,
275 	.driver		= { .owner = THIS_MODULE },
276 }, {
277 	.phy_id		= PHY_ID_BCM7445,
278 	.phy_id_mask	= 0xfffffff0,
279 	.name		= "Broadcom BCM7445",
280 	.features	= PHY_GBIT_FEATURES |
281 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
282 	.flags		= PHY_IS_INTERNAL,
283 	.config_init	= bcm7xxx_28nm_config_init,
284 	.config_aneg	= genphy_config_aneg,
285 	.read_status	= genphy_read_status,
286 	.suspend	= bcm7xxx_suspend,
287 	.resume		= bcm7xxx_28nm_config_init,
288 	.driver		= { .owner = THIS_MODULE },
289 }, {
290 	.name		= "Broadcom BCM7XXX 28nm",
291 	.phy_id		= PHY_ID_BCM7XXX_28,
292 	.phy_id_mask	= PHY_BCM_OUI_MASK,
293 	.features	= PHY_GBIT_FEATURES |
294 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
295 	.flags		= PHY_IS_INTERNAL,
296 	.config_init	= bcm7xxx_28nm_config_init,
297 	.config_aneg	= genphy_config_aneg,
298 	.read_status	= genphy_read_status,
299 	.suspend	= bcm7xxx_suspend,
300 	.resume		= bcm7xxx_28nm_config_init,
301 	.driver		= { .owner = THIS_MODULE },
302 }, {
303 	.phy_id		= PHY_BCM_OUI_4,
304 	.phy_id_mask	= 0xffff0000,
305 	.name		= "Broadcom BCM7XXX 40nm",
306 	.features	= PHY_GBIT_FEATURES |
307 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
308 	.flags		= PHY_IS_INTERNAL,
309 	.config_init	= bcm7xxx_config_init,
310 	.config_aneg	= genphy_config_aneg,
311 	.read_status	= genphy_read_status,
312 	.suspend	= bcm7xxx_suspend,
313 	.resume		= bcm7xxx_config_init,
314 	.driver		= { .owner = THIS_MODULE },
315 }, {
316 	.phy_id		= PHY_BCM_OUI_5,
317 	.phy_id_mask	= 0xffffff00,
318 	.name		= "Broadcom BCM7XXX 65nm",
319 	.features	= PHY_BASIC_FEATURES |
320 			  SUPPORTED_Pause | SUPPORTED_Asym_Pause,
321 	.flags		= PHY_IS_INTERNAL,
322 	.config_init	= bcm7xxx_dummy_config_init,
323 	.config_aneg	= genphy_config_aneg,
324 	.read_status	= genphy_read_status,
325 	.suspend	= bcm7xxx_suspend,
326 	.resume		= bcm7xxx_config_init,
327 	.driver		= { .owner = THIS_MODULE },
328 } };
329 
330 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
331 	{ PHY_ID_BCM7366, 0xfffffff0, },
332 	{ PHY_ID_BCM7439, 0xfffffff0, },
333 	{ PHY_ID_BCM7445, 0xfffffff0, },
334 	{ PHY_ID_BCM7XXX_28, 0xfffffc00 },
335 	{ PHY_BCM_OUI_4, 0xffff0000 },
336 	{ PHY_BCM_OUI_5, 0xffffff00 },
337 	{ }
338 };
339 
340 static int __init bcm7xxx_phy_init(void)
341 {
342 	return phy_drivers_register(bcm7xxx_driver,
343 			ARRAY_SIZE(bcm7xxx_driver));
344 }
345 
346 static void __exit bcm7xxx_phy_exit(void)
347 {
348 	phy_drivers_unregister(bcm7xxx_driver,
349 			ARRAY_SIZE(bcm7xxx_driver));
350 }
351 
352 module_init(bcm7xxx_phy_init);
353 module_exit(bcm7xxx_phy_exit);
354 
355 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
356 
357 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
358 MODULE_LICENSE("GPL");
359 MODULE_AUTHOR("Broadcom Corporation");
360