xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision 039a7b85)
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 "bcm-phy-lib.h"
16 #include <linux/bitops.h>
17 #include <linux/brcmphy.h>
18 #include <linux/mdio.h>
19 
20 /* Broadcom BCM7xxx internal PHY registers */
21 
22 /* 40nm only register definitions */
23 #define MII_BCM7XXX_100TX_AUX_CTL	0x10
24 #define MII_BCM7XXX_100TX_FALSE_CAR	0x13
25 #define MII_BCM7XXX_100TX_DISC		0x14
26 #define MII_BCM7XXX_AUX_MODE		0x1d
27 #define  MII_BCM7XXX_64CLK_MDIO		BIT(12)
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_RXCONFIG_2			MISC_ADDR(0x38, 2)
42 #define AFE_RX_LP_COUNTER		MISC_ADDR(0x38, 3)
43 #define AFE_TX_CONFIG			MISC_ADDR(0x39, 0)
44 #define AFE_VDCA_ICTRL_0		MISC_ADDR(0x39, 1)
45 #define AFE_VDAC_OTHERS_0		MISC_ADDR(0x39, 3)
46 #define AFE_HPF_TRIM_OTHERS		MISC_ADDR(0x3a, 0)
47 
48 struct bcm7xxx_phy_priv {
49 	u64	*stats;
50 };
51 
52 static void r_rc_cal_reset(struct phy_device *phydev)
53 {
54 	/* Reset R_CAL/RC_CAL Engine */
55 	bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
56 
57 	/* Disable Reset R_AL/RC_CAL Engine */
58 	bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
59 }
60 
61 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
62 {
63 	/* Increase VCO range to prevent unlocking problem of PLL at low
64 	 * temp
65 	 */
66 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
67 
68 	/* Change Ki to 011 */
69 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
70 
71 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
72 	 * to 111
73 	 */
74 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
75 
76 	/* Adjust bias current trim by -3 */
77 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
78 
79 	/* Switch to CORE_BASE1E */
80 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
81 
82 	r_rc_cal_reset(phydev);
83 
84 	/* write AFE_RXCONFIG_0 */
85 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
86 
87 	/* write AFE_RXCONFIG_1 */
88 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
89 
90 	/* write AFE_RX_LP_COUNTER */
91 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
92 
93 	/* write AFE_HPF_TRIM_OTHERS */
94 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
95 
96 	/* write AFTE_TX_CONFIG */
97 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
98 
99 	return 0;
100 }
101 
102 static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
103 {
104 	/* AFE_RXCONFIG_0 */
105 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
106 
107 	/* AFE_RXCONFIG_1 */
108 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
109 
110 	/* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
111 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
112 
113 	/* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
114 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
115 
116 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
117 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
118 
119 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
120 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
121 
122 	/* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
123 	bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
124 
125 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
126 	 * offset for HT=0 code
127 	 */
128 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
129 
130 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
131 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
132 
133 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
134 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
135 
136 	/* Reset R_CAL/RC_CAL engine */
137 	r_rc_cal_reset(phydev);
138 
139 	return 0;
140 }
141 
142 static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
143 {
144 	/* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
145 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
146 
147 	/* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
148 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
149 
150 	/* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
151 	bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
152 
153 	/* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
154 	 * offset for HT=0 code
155 	 */
156 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
157 
158 	/* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
159 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
160 
161 	/* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
162 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
163 
164 	/* Reset R_CAL/RC_CAL engine */
165 	r_rc_cal_reset(phydev);
166 
167 	return 0;
168 }
169 
170 static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)
171 {
172 	/* +1 RC_CAL codes for RL centering for both LT and HT conditions */
173 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);
174 
175 	/* Cut master bias current by 2% to compensate for RC_CAL offset */
176 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);
177 
178 	/* Improve hybrid leakage */
179 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);
180 
181 	/* Change rx_on_tune 8 to 0xf */
182 	bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);
183 
184 	/* Change 100Tx EEE bandwidth */
185 	bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);
186 
187 	/* Enable ffe zero detection for Vitesse interoperability */
188 	bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);
189 
190 	r_rc_cal_reset(phydev);
191 
192 	return 0;
193 }
194 
195 static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
196 {
197 	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
198 	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
199 	u8 count;
200 	int ret = 0;
201 
202 	/* Newer devices have moved the revision information back into a
203 	 * standard location in MII_PHYS_ID[23]
204 	 */
205 	if (rev == 0)
206 		rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
207 
208 	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
209 		     phydev_name(phydev), phydev->drv->name, rev, patch);
210 
211 	/* Dummy read to a register to workaround an issue upon reset where the
212 	 * internal inverter may not allow the first MDIO transaction to pass
213 	 * the MDIO management controller and make us return 0xffff for such
214 	 * reads.
215 	 */
216 	phy_read(phydev, MII_BMSR);
217 
218 	switch (rev) {
219 	case 0xb0:
220 		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
221 		break;
222 	case 0xd0:
223 		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
224 		break;
225 	case 0xe0:
226 	case 0xf0:
227 	/* Rev G0 introduces a roll over */
228 	case 0x10:
229 		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
230 		break;
231 	case 0x01:
232 		ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
233 		break;
234 	default:
235 		break;
236 	}
237 
238 	if (ret)
239 		return ret;
240 
241 	ret = bcm_phy_downshift_get(phydev, &count);
242 	if (ret)
243 		return ret;
244 
245 	/* Only enable EEE if Wirespeed/downshift is disabled */
246 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
247 	if (ret)
248 		return ret;
249 
250 	return bcm_phy_enable_apd(phydev, true);
251 }
252 
253 static int bcm7xxx_28nm_resume(struct phy_device *phydev)
254 {
255 	int ret;
256 
257 	/* Re-apply workarounds coming out suspend/resume */
258 	ret = bcm7xxx_28nm_config_init(phydev);
259 	if (ret)
260 		return ret;
261 
262 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
263 	 * or "hub" compliant advertised mode, fix that. This does not
264 	 * cause any problems with the PHY library since genphy_config_aneg()
265 	 * gracefully handles auto-negotiated and forced modes.
266 	 */
267 	return genphy_config_aneg(phydev);
268 }
269 
270 static int phy_set_clr_bits(struct phy_device *dev, int location,
271 					int set_mask, int clr_mask)
272 {
273 	int v, ret;
274 
275 	v = phy_read(dev, location);
276 	if (v < 0)
277 		return v;
278 
279 	v &= ~clr_mask;
280 	v |= set_mask;
281 
282 	ret = phy_write(dev, location, v);
283 	if (ret < 0)
284 		return ret;
285 
286 	return v;
287 }
288 
289 static int bcm7xxx_config_init(struct phy_device *phydev)
290 {
291 	int ret;
292 
293 	/* Enable 64 clock MDIO */
294 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);
295 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
296 
297 	/* set shadow mode 2 */
298 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
299 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
300 	if (ret < 0)
301 		return ret;
302 
303 	/* set iddq_clkbias */
304 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
305 	udelay(10);
306 
307 	/* reset iddq_clkbias */
308 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
309 
310 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
311 
312 	/* reset shadow mode 2 */
313 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);
314 	if (ret < 0)
315 		return ret;
316 
317 	return 0;
318 }
319 
320 /* Workaround for putting the PHY in IDDQ mode, required
321  * for all BCM7XXX 40nm and 65nm PHYs
322  */
323 static int bcm7xxx_suspend(struct phy_device *phydev)
324 {
325 	int ret;
326 	const struct bcm7xxx_regs {
327 		int reg;
328 		u16 value;
329 	} bcm7xxx_suspend_cfg[] = {
330 		{ MII_BCM7XXX_TEST, 0x008b },
331 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
332 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
333 		{ MII_BCM7XXX_TEST, 0x000f },
334 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
335 		{ MII_BCM7XXX_TEST, 0x000b },
336 	};
337 	unsigned int i;
338 
339 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
340 		ret = phy_write(phydev,
341 				bcm7xxx_suspend_cfg[i].reg,
342 				bcm7xxx_suspend_cfg[i].value);
343 		if (ret)
344 			return ret;
345 	}
346 
347 	return 0;
348 }
349 
350 static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
351 				    struct ethtool_tunable *tuna,
352 				    void *data)
353 {
354 	switch (tuna->id) {
355 	case ETHTOOL_PHY_DOWNSHIFT:
356 		return bcm_phy_downshift_get(phydev, (u8 *)data);
357 	default:
358 		return -EOPNOTSUPP;
359 	}
360 }
361 
362 static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
363 				    struct ethtool_tunable *tuna,
364 				    const void *data)
365 {
366 	u8 count = *(u8 *)data;
367 	int ret;
368 
369 	switch (tuna->id) {
370 	case ETHTOOL_PHY_DOWNSHIFT:
371 		ret = bcm_phy_downshift_set(phydev, count);
372 		break;
373 	default:
374 		return -EOPNOTSUPP;
375 	}
376 
377 	if (ret)
378 		return ret;
379 
380 	/* Disable EEE advertisment since this prevents the PHY
381 	 * from successfully linking up, trigger auto-negotiation restart
382 	 * to let the MAC decide what to do.
383 	 */
384 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
385 	if (ret)
386 		return ret;
387 
388 	return genphy_restart_aneg(phydev);
389 }
390 
391 static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
392 				       struct ethtool_stats *stats, u64 *data)
393 {
394 	struct bcm7xxx_phy_priv *priv = phydev->priv;
395 
396 	bcm_phy_get_stats(phydev, priv->stats, stats, data);
397 }
398 
399 static int bcm7xxx_28nm_probe(struct phy_device *phydev)
400 {
401 	struct bcm7xxx_phy_priv *priv;
402 
403 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
404 	if (!priv)
405 		return -ENOMEM;
406 
407 	phydev->priv = priv;
408 
409 	priv->stats = devm_kcalloc(&phydev->mdio.dev,
410 				   bcm_phy_get_sset_count(phydev), sizeof(u64),
411 				   GFP_KERNEL);
412 	if (!priv->stats)
413 		return -ENOMEM;
414 
415 	return 0;
416 }
417 
418 #define BCM7XXX_28NM_GPHY(_oui, _name)					\
419 {									\
420 	.phy_id		= (_oui),					\
421 	.phy_id_mask	= 0xfffffff0,					\
422 	.name		= _name,					\
423 	.features	= PHY_GBIT_FEATURES,				\
424 	.flags		= PHY_IS_INTERNAL,				\
425 	.config_init	= bcm7xxx_28nm_config_init,			\
426 	.config_aneg	= genphy_config_aneg,				\
427 	.read_status	= genphy_read_status,				\
428 	.resume		= bcm7xxx_28nm_resume,				\
429 	.get_tunable	= bcm7xxx_28nm_get_tunable,			\
430 	.set_tunable	= bcm7xxx_28nm_set_tunable,			\
431 	.get_sset_count	= bcm_phy_get_sset_count,			\
432 	.get_strings	= bcm_phy_get_strings,				\
433 	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\
434 	.probe		= bcm7xxx_28nm_probe,				\
435 }
436 
437 #define BCM7XXX_40NM_EPHY(_oui, _name)					\
438 {									\
439 	.phy_id         = (_oui),					\
440 	.phy_id_mask    = 0xfffffff0,					\
441 	.name           = _name,					\
442 	.features       = PHY_BASIC_FEATURES,				\
443 	.flags          = PHY_IS_INTERNAL,				\
444 	.config_init    = bcm7xxx_config_init,				\
445 	.config_aneg    = genphy_config_aneg,				\
446 	.read_status    = genphy_read_status,				\
447 	.suspend        = bcm7xxx_suspend,				\
448 	.resume         = bcm7xxx_config_init,				\
449 }
450 
451 static struct phy_driver bcm7xxx_driver[] = {
452 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
453 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),
454 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
455 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
456 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
457 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
458 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
459 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),
460 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),
461 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),
462 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),
463 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),
464 };
465 
466 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
467 	{ PHY_ID_BCM7250, 0xfffffff0, },
468 	{ PHY_ID_BCM7278, 0xfffffff0, },
469 	{ PHY_ID_BCM7364, 0xfffffff0, },
470 	{ PHY_ID_BCM7366, 0xfffffff0, },
471 	{ PHY_ID_BCM7346, 0xfffffff0, },
472 	{ PHY_ID_BCM7362, 0xfffffff0, },
473 	{ PHY_ID_BCM7425, 0xfffffff0, },
474 	{ PHY_ID_BCM7429, 0xfffffff0, },
475 	{ PHY_ID_BCM7439, 0xfffffff0, },
476 	{ PHY_ID_BCM7435, 0xfffffff0, },
477 	{ PHY_ID_BCM7445, 0xfffffff0, },
478 	{ }
479 };
480 
481 module_phy_driver(bcm7xxx_driver);
482 
483 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
484 
485 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
486 MODULE_LICENSE("GPL");
487 MODULE_AUTHOR("Broadcom Corporation");
488