xref: /openbmc/linux/drivers/net/phy/bcm7xxx.c (revision e4781421e883340b796da5a724bda7226817990b)
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_config_init(struct phy_device *phydev)
171 {
172 	u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
173 	u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
174 	u8 count;
175 	int ret = 0;
176 
177 	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
178 		     phydev_name(phydev), phydev->drv->name, rev, patch);
179 
180 	/* Dummy read to a register to workaround an issue upon reset where the
181 	 * internal inverter may not allow the first MDIO transaction to pass
182 	 * the MDIO management controller and make us return 0xffff for such
183 	 * reads.
184 	 */
185 	phy_read(phydev, MII_BMSR);
186 
187 	switch (rev) {
188 	case 0xb0:
189 		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
190 		break;
191 	case 0xd0:
192 		ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
193 		break;
194 	case 0xe0:
195 	case 0xf0:
196 	/* Rev G0 introduces a roll over */
197 	case 0x10:
198 		ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
199 		break;
200 	default:
201 		break;
202 	}
203 
204 	if (ret)
205 		return ret;
206 
207 	ret = bcm_phy_downshift_get(phydev, &count);
208 	if (ret)
209 		return ret;
210 
211 	/* Only enable EEE if Wirespeed/downshift is disabled */
212 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
213 	if (ret)
214 		return ret;
215 
216 	return bcm_phy_enable_apd(phydev, true);
217 }
218 
219 static int bcm7xxx_28nm_resume(struct phy_device *phydev)
220 {
221 	int ret;
222 
223 	/* Re-apply workarounds coming out suspend/resume */
224 	ret = bcm7xxx_28nm_config_init(phydev);
225 	if (ret)
226 		return ret;
227 
228 	/* 28nm Gigabit PHYs come out of reset without any half-duplex
229 	 * or "hub" compliant advertised mode, fix that. This does not
230 	 * cause any problems with the PHY library since genphy_config_aneg()
231 	 * gracefully handles auto-negotiated and forced modes.
232 	 */
233 	return genphy_config_aneg(phydev);
234 }
235 
236 static int phy_set_clr_bits(struct phy_device *dev, int location,
237 					int set_mask, int clr_mask)
238 {
239 	int v, ret;
240 
241 	v = phy_read(dev, location);
242 	if (v < 0)
243 		return v;
244 
245 	v &= ~clr_mask;
246 	v |= set_mask;
247 
248 	ret = phy_write(dev, location, v);
249 	if (ret < 0)
250 		return ret;
251 
252 	return v;
253 }
254 
255 static int bcm7xxx_config_init(struct phy_device *phydev)
256 {
257 	int ret;
258 
259 	/* Enable 64 clock MDIO */
260 	phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);
261 	phy_read(phydev, MII_BCM7XXX_AUX_MODE);
262 
263 	/* set shadow mode 2 */
264 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
265 			MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
266 	if (ret < 0)
267 		return ret;
268 
269 	/* set iddq_clkbias */
270 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
271 	udelay(10);
272 
273 	/* reset iddq_clkbias */
274 	phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
275 
276 	phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
277 
278 	/* reset shadow mode 2 */
279 	ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);
280 	if (ret < 0)
281 		return ret;
282 
283 	return 0;
284 }
285 
286 /* Workaround for putting the PHY in IDDQ mode, required
287  * for all BCM7XXX 40nm and 65nm PHYs
288  */
289 static int bcm7xxx_suspend(struct phy_device *phydev)
290 {
291 	int ret;
292 	const struct bcm7xxx_regs {
293 		int reg;
294 		u16 value;
295 	} bcm7xxx_suspend_cfg[] = {
296 		{ MII_BCM7XXX_TEST, 0x008b },
297 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
298 		{ MII_BCM7XXX_100TX_DISC, 0x7000 },
299 		{ MII_BCM7XXX_TEST, 0x000f },
300 		{ MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
301 		{ MII_BCM7XXX_TEST, 0x000b },
302 	};
303 	unsigned int i;
304 
305 	for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
306 		ret = phy_write(phydev,
307 				bcm7xxx_suspend_cfg[i].reg,
308 				bcm7xxx_suspend_cfg[i].value);
309 		if (ret)
310 			return ret;
311 	}
312 
313 	return 0;
314 }
315 
316 static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
317 				    struct ethtool_tunable *tuna,
318 				    void *data)
319 {
320 	switch (tuna->id) {
321 	case ETHTOOL_PHY_DOWNSHIFT:
322 		return bcm_phy_downshift_get(phydev, (u8 *)data);
323 	default:
324 		return -EOPNOTSUPP;
325 	}
326 }
327 
328 static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
329 				    struct ethtool_tunable *tuna,
330 				    const void *data)
331 {
332 	u8 count = *(u8 *)data;
333 	int ret;
334 
335 	switch (tuna->id) {
336 	case ETHTOOL_PHY_DOWNSHIFT:
337 		ret = bcm_phy_downshift_set(phydev, count);
338 		break;
339 	default:
340 		return -EOPNOTSUPP;
341 	}
342 
343 	if (ret)
344 		return ret;
345 
346 	/* Disable EEE advertisment since this prevents the PHY
347 	 * from successfully linking up, trigger auto-negotiation restart
348 	 * to let the MAC decide what to do.
349 	 */
350 	ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
351 	if (ret)
352 		return ret;
353 
354 	return genphy_restart_aneg(phydev);
355 }
356 
357 static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
358 				       struct ethtool_stats *stats, u64 *data)
359 {
360 	struct bcm7xxx_phy_priv *priv = phydev->priv;
361 
362 	bcm_phy_get_stats(phydev, priv->stats, stats, data);
363 }
364 
365 static int bcm7xxx_28nm_probe(struct phy_device *phydev)
366 {
367 	struct bcm7xxx_phy_priv *priv;
368 
369 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
370 	if (!priv)
371 		return -ENOMEM;
372 
373 	phydev->priv = priv;
374 
375 	priv->stats = devm_kcalloc(&phydev->mdio.dev,
376 				   bcm_phy_get_sset_count(phydev), sizeof(u64),
377 				   GFP_KERNEL);
378 	if (!priv->stats)
379 		return -ENOMEM;
380 
381 	return 0;
382 }
383 
384 #define BCM7XXX_28NM_GPHY(_oui, _name)					\
385 {									\
386 	.phy_id		= (_oui),					\
387 	.phy_id_mask	= 0xfffffff0,					\
388 	.name		= _name,					\
389 	.features	= PHY_GBIT_FEATURES,				\
390 	.flags		= PHY_IS_INTERNAL,				\
391 	.config_init	= bcm7xxx_28nm_config_init,			\
392 	.config_aneg	= genphy_config_aneg,				\
393 	.read_status	= genphy_read_status,				\
394 	.resume		= bcm7xxx_28nm_resume,				\
395 	.get_tunable	= bcm7xxx_28nm_get_tunable,			\
396 	.set_tunable	= bcm7xxx_28nm_set_tunable,			\
397 	.get_sset_count	= bcm_phy_get_sset_count,			\
398 	.get_strings	= bcm_phy_get_strings,				\
399 	.get_stats	= bcm7xxx_28nm_get_phy_stats,			\
400 	.probe		= bcm7xxx_28nm_probe,				\
401 }
402 
403 #define BCM7XXX_40NM_EPHY(_oui, _name)					\
404 {									\
405 	.phy_id         = (_oui),					\
406 	.phy_id_mask    = 0xfffffff0,					\
407 	.name           = _name,					\
408 	.features       = PHY_BASIC_FEATURES,				\
409 	.flags          = PHY_IS_INTERNAL,				\
410 	.config_init    = bcm7xxx_config_init,				\
411 	.config_aneg    = genphy_config_aneg,				\
412 	.read_status    = genphy_read_status,				\
413 	.suspend        = bcm7xxx_suspend,				\
414 	.resume         = bcm7xxx_config_init,				\
415 }
416 
417 static struct phy_driver bcm7xxx_driver[] = {
418 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
419 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
420 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
421 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
422 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
423 	BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
424 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),
425 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),
426 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),
427 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),
428 	BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),
429 };
430 
431 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
432 	{ PHY_ID_BCM7250, 0xfffffff0, },
433 	{ PHY_ID_BCM7364, 0xfffffff0, },
434 	{ PHY_ID_BCM7366, 0xfffffff0, },
435 	{ PHY_ID_BCM7346, 0xfffffff0, },
436 	{ PHY_ID_BCM7362, 0xfffffff0, },
437 	{ PHY_ID_BCM7425, 0xfffffff0, },
438 	{ PHY_ID_BCM7429, 0xfffffff0, },
439 	{ PHY_ID_BCM7439, 0xfffffff0, },
440 	{ PHY_ID_BCM7435, 0xfffffff0, },
441 	{ PHY_ID_BCM7445, 0xfffffff0, },
442 	{ }
443 };
444 
445 module_phy_driver(bcm7xxx_driver);
446 
447 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
448 
449 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
450 MODULE_LICENSE("GPL");
451 MODULE_AUTHOR("Broadcom Corporation");
452