xref: /linux/drivers/net/phy/bcm-phy-lib.c (revision 7bb377107c72a40ab7505341f8626c8eb79a0cb7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2015-2017 Broadcom
4  */
5 
6 #include "bcm-phy-lib.h"
7 #include <linux/brcmphy.h>
8 #include <linux/export.h>
9 #include <linux/mdio.h>
10 #include <linux/module.h>
11 #include <linux/phy.h>
12 #include <linux/ethtool.h>
13 
14 #define MII_BCM_CHANNEL_WIDTH     0x2000
15 #define BCM_CL45VEN_EEE_ADV       0x3c
16 
17 int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
18 {
19 	int rc;
20 
21 	rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
22 	if (rc < 0)
23 		return rc;
24 
25 	return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
26 }
27 EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
28 
29 int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
30 {
31 	int val;
32 
33 	val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
34 	if (val < 0)
35 		return val;
36 
37 	val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
38 
39 	/* Restore default value.  It's O.K. if this write fails. */
40 	phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
41 
42 	return val;
43 }
44 EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
45 
46 int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
47 {
48 	/* The register must be written to both the Shadow Register Select and
49 	 * the Shadow Read Register Selector
50 	 */
51 	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |
52 		  regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
53 	return phy_read(phydev, MII_BCM54XX_AUX_CTL);
54 }
55 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
56 
57 int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
58 {
59 	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
60 }
61 EXPORT_SYMBOL(bcm54xx_auxctl_write);
62 
63 int bcm_phy_write_misc(struct phy_device *phydev,
64 		       u16 reg, u16 chl, u16 val)
65 {
66 	int rc;
67 	int tmp;
68 
69 	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
70 		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
71 	if (rc < 0)
72 		return rc;
73 
74 	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
75 	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
76 	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
77 	if (rc < 0)
78 		return rc;
79 
80 	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
81 	rc = bcm_phy_write_exp(phydev, tmp, val);
82 
83 	return rc;
84 }
85 EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
86 
87 int bcm_phy_read_misc(struct phy_device *phydev,
88 		      u16 reg, u16 chl)
89 {
90 	int rc;
91 	int tmp;
92 
93 	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
94 		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
95 	if (rc < 0)
96 		return rc;
97 
98 	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
99 	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
100 	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
101 	if (rc < 0)
102 		return rc;
103 
104 	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
105 	rc = bcm_phy_read_exp(phydev, tmp);
106 
107 	return rc;
108 }
109 EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
110 
111 int bcm_phy_ack_intr(struct phy_device *phydev)
112 {
113 	int reg;
114 
115 	/* Clear pending interrupts.  */
116 	reg = phy_read(phydev, MII_BCM54XX_ISR);
117 	if (reg < 0)
118 		return reg;
119 
120 	return 0;
121 }
122 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
123 
124 int bcm_phy_config_intr(struct phy_device *phydev)
125 {
126 	int reg;
127 
128 	reg = phy_read(phydev, MII_BCM54XX_ECR);
129 	if (reg < 0)
130 		return reg;
131 
132 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
133 		reg &= ~MII_BCM54XX_ECR_IM;
134 	else
135 		reg |= MII_BCM54XX_ECR_IM;
136 
137 	return phy_write(phydev, MII_BCM54XX_ECR, reg);
138 }
139 EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
140 
141 int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
142 {
143 	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
144 	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
145 }
146 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
147 
148 int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
149 			 u16 val)
150 {
151 	return phy_write(phydev, MII_BCM54XX_SHD,
152 			 MII_BCM54XX_SHD_WRITE |
153 			 MII_BCM54XX_SHD_VAL(shadow) |
154 			 MII_BCM54XX_SHD_DATA(val));
155 }
156 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
157 
158 int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
159 {
160 	int val;
161 
162 	val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
163 	if (val < 0)
164 		return val;
165 
166 	return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
167 }
168 EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
169 
170 int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
171 {
172 	int ret;
173 
174 	phy_lock_mdio_bus(phydev);
175 	ret = __bcm_phy_read_rdb(phydev, rdb);
176 	phy_unlock_mdio_bus(phydev);
177 
178 	return ret;
179 }
180 EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
181 
182 int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
183 {
184 	int ret;
185 
186 	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
187 	if (ret < 0)
188 		return ret;
189 
190 	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
191 }
192 EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
193 
194 int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
195 {
196 	int ret;
197 
198 	phy_lock_mdio_bus(phydev);
199 	ret = __bcm_phy_write_rdb(phydev, rdb, val);
200 	phy_unlock_mdio_bus(phydev);
201 
202 	return ret;
203 }
204 EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
205 
206 int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
207 {
208 	int new, ret;
209 
210 	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
211 	if (ret < 0)
212 		return ret;
213 
214 	ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
215 	if (ret < 0)
216 		return ret;
217 
218 	new = (ret & ~mask) | set;
219 	if (new == ret)
220 		return 0;
221 
222 	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
223 }
224 EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
225 
226 int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
227 {
228 	int ret;
229 
230 	phy_lock_mdio_bus(phydev);
231 	ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
232 	phy_unlock_mdio_bus(phydev);
233 
234 	return ret;
235 }
236 EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
237 
238 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
239 {
240 	int val;
241 
242 	if (dll_pwr_down) {
243 		val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
244 		if (val < 0)
245 			return val;
246 
247 		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
248 		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
249 	}
250 
251 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
252 	if (val < 0)
253 		return val;
254 
255 	/* Clear APD bits */
256 	val &= BCM_APD_CLR_MASK;
257 
258 	if (phydev->autoneg == AUTONEG_ENABLE)
259 		val |= BCM54XX_SHD_APD_EN;
260 	else
261 		val |= BCM_NO_ANEG_APD_EN;
262 
263 	/* Enable energy detect single link pulse for easy wakeup */
264 	val |= BCM_APD_SINGLELP_EN;
265 
266 	/* Enable Auto Power-Down (APD) for the PHY */
267 	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
268 }
269 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
270 
271 int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
272 {
273 	int val;
274 
275 	/* Enable EEE at PHY level */
276 	val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);
277 	if (val < 0)
278 		return val;
279 
280 	if (enable)
281 		val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
282 	else
283 		val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
284 
285 	phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);
286 
287 	/* Advertise EEE */
288 	val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);
289 	if (val < 0)
290 		return val;
291 
292 	if (enable)
293 		val |= (MDIO_EEE_100TX | MDIO_EEE_1000T);
294 	else
295 		val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T);
296 
297 	phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);
298 
299 	return 0;
300 }
301 EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
302 
303 int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
304 {
305 	int val;
306 
307 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
308 	if (val < 0)
309 		return val;
310 
311 	/* Check if wirespeed is enabled or not */
312 	if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
313 		*count = DOWNSHIFT_DEV_DISABLE;
314 		return 0;
315 	}
316 
317 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
318 	if (val < 0)
319 		return val;
320 
321 	/* Downgrade after one link attempt */
322 	if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
323 		*count = 1;
324 	} else {
325 		/* Downgrade after configured retry count */
326 		val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
327 		val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
328 		*count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
329 	}
330 
331 	return 0;
332 }
333 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
334 
335 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
336 {
337 	int val = 0, ret = 0;
338 
339 	/* Range check the number given */
340 	if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
341 	    BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
342 	    count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
343 		return -ERANGE;
344 	}
345 
346 	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
347 	if (val < 0)
348 		return val;
349 
350 	/* Se the write enable bit */
351 	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
352 
353 	if (count == DOWNSHIFT_DEV_DISABLE) {
354 		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
355 		return bcm54xx_auxctl_write(phydev,
356 					    MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
357 					    val);
358 	} else {
359 		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
360 		ret = bcm54xx_auxctl_write(phydev,
361 					   MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
362 					   val);
363 		if (ret < 0)
364 			return ret;
365 	}
366 
367 	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
368 	val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
369 		 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
370 		 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
371 
372 	switch (count) {
373 	case 1:
374 		val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
375 		break;
376 	case DOWNSHIFT_DEV_DEFAULT_COUNT:
377 		val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
378 		break;
379 	default:
380 		val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
381 			BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
382 		break;
383 	}
384 
385 	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
386 }
387 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
388 
389 struct bcm_phy_hw_stat {
390 	const char *string;
391 	u8 reg;
392 	u8 shift;
393 	u8 bits;
394 };
395 
396 /* Counters freeze at either 0xffff or 0xff, better than nothing */
397 static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
398 	{ "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
399 	{ "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
400 	{ "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
401 	{ "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
402 	{ "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
403 };
404 
405 int bcm_phy_get_sset_count(struct phy_device *phydev)
406 {
407 	return ARRAY_SIZE(bcm_phy_hw_stats);
408 }
409 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
410 
411 void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
412 {
413 	unsigned int i;
414 
415 	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
416 		strlcpy(data + i * ETH_GSTRING_LEN,
417 			bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
418 }
419 EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
420 
421 /* Caller is supposed to provide appropriate storage for the library code to
422  * access the shadow copy
423  */
424 static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
425 			    unsigned int i)
426 {
427 	struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
428 	int val;
429 	u64 ret;
430 
431 	val = phy_read(phydev, stat.reg);
432 	if (val < 0) {
433 		ret = U64_MAX;
434 	} else {
435 		val >>= stat.shift;
436 		val = val & ((1 << stat.bits) - 1);
437 		shadow[i] += val;
438 		ret = shadow[i];
439 	}
440 
441 	return ret;
442 }
443 
444 void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
445 		       struct ethtool_stats *stats, u64 *data)
446 {
447 	unsigned int i;
448 
449 	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
450 		data[i] = bcm_phy_get_stat(phydev, shadow, i);
451 }
452 EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
453 
454 void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
455 {
456 	/* Reset R_CAL/RC_CAL Engine */
457 	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
458 
459 	/* Disable Reset R_AL/RC_CAL Engine */
460 	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
461 }
462 EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
463 
464 int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
465 {
466 	/* Increase VCO range to prevent unlocking problem of PLL at low
467 	 * temp
468 	 */
469 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
470 
471 	/* Change Ki to 011 */
472 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
473 
474 	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
475 	 * to 111
476 	 */
477 	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
478 
479 	/* Adjust bias current trim by -3 */
480 	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
481 
482 	/* Switch to CORE_BASE1E */
483 	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
484 
485 	bcm_phy_r_rc_cal_reset(phydev);
486 
487 	/* write AFE_RXCONFIG_0 */
488 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
489 
490 	/* write AFE_RXCONFIG_1 */
491 	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
492 
493 	/* write AFE_RX_LP_COUNTER */
494 	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
495 
496 	/* write AFE_HPF_TRIM_OTHERS */
497 	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
498 
499 	/* write AFTE_TX_CONFIG */
500 	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
501 
502 	return 0;
503 }
504 EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
505 
506 int bcm_phy_enable_jumbo(struct phy_device *phydev)
507 {
508 	int ret;
509 
510 	ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);
511 	if (ret < 0)
512 		return ret;
513 
514 	/* Enable extended length packet reception */
515 	ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
516 				   ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);
517 	if (ret < 0)
518 		return ret;
519 
520 	/* Enable the elastic FIFO for raising the transmission limit from
521 	 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation
522 	 * latency.
523 	 */
524 	return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);
525 }
526 EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);
527 
528 MODULE_DESCRIPTION("Broadcom PHY Library");
529 MODULE_LICENSE("GPL v2");
530 MODULE_AUTHOR("Broadcom Corporation");
531