xref: /linux/drivers/net/phy/qcom/qcom-phy-lib.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/phy.h>
4 #include <linux/module.h>
5 
6 #include <linux/netdevice.h>
7 #include <linux/etherdevice.h>
8 #include <linux/ethtool_netlink.h>
9 
10 #include "qcom.h"
11 
12 MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
13 MODULE_AUTHOR("Matus Ujhelyi");
14 MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
15 MODULE_LICENSE("GPL");
16 
17 int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
18 {
19 	int ret;
20 
21 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
22 	if (ret < 0)
23 		return ret;
24 
25 	return phy_read(phydev, AT803X_DEBUG_DATA);
26 }
27 EXPORT_SYMBOL_GPL(at803x_debug_reg_read);
28 
29 int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
30 			  u16 clear, u16 set)
31 {
32 	u16 val;
33 	int ret;
34 
35 	ret = at803x_debug_reg_read(phydev, reg);
36 	if (ret < 0)
37 		return ret;
38 
39 	val = ret & 0xffff;
40 	val &= ~clear;
41 	val |= set;
42 
43 	return phy_write(phydev, AT803X_DEBUG_DATA, val);
44 }
45 EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);
46 
47 int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
48 {
49 	int ret;
50 
51 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
52 	if (ret < 0)
53 		return ret;
54 
55 	return phy_write(phydev, AT803X_DEBUG_DATA, data);
56 }
57 EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
58 
59 int at803x_set_wol(struct phy_device *phydev,
60 		   struct ethtool_wolinfo *wol)
61 {
62 	int ret, irq_enabled;
63 
64 	if (wol->wolopts & WAKE_MAGIC) {
65 		struct net_device *ndev = phydev->attached_dev;
66 		const u8 *mac;
67 		unsigned int i;
68 		static const unsigned int offsets[] = {
69 			AT803X_LOC_MAC_ADDR_32_47_OFFSET,
70 			AT803X_LOC_MAC_ADDR_16_31_OFFSET,
71 			AT803X_LOC_MAC_ADDR_0_15_OFFSET,
72 		};
73 
74 		if (!ndev)
75 			return -ENODEV;
76 
77 		mac = (const u8 *)ndev->dev_addr;
78 
79 		if (!is_valid_ether_addr(mac))
80 			return -EINVAL;
81 
82 		for (i = 0; i < 3; i++)
83 			phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
84 				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
85 
86 		/* Enable WOL interrupt */
87 		ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
88 		if (ret)
89 			return ret;
90 	} else {
91 		/* Disable WOL interrupt */
92 		ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
93 		if (ret)
94 			return ret;
95 	}
96 
97 	/* Clear WOL status */
98 	ret = phy_read(phydev, AT803X_INTR_STATUS);
99 	if (ret < 0)
100 		return ret;
101 
102 	/* Check if there are other interrupts except for WOL triggered when PHY is
103 	 * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
104 	 * be passed up to the interrupt PIN.
105 	 */
106 	irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
107 	if (irq_enabled < 0)
108 		return irq_enabled;
109 
110 	irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
111 	if (ret & irq_enabled && !phy_polling_mode(phydev))
112 		phy_trigger_machine(phydev);
113 
114 	return 0;
115 }
116 EXPORT_SYMBOL_GPL(at803x_set_wol);
117 
118 void at803x_get_wol(struct phy_device *phydev,
119 		    struct ethtool_wolinfo *wol)
120 {
121 	int value;
122 
123 	wol->supported = WAKE_MAGIC;
124 	wol->wolopts = 0;
125 
126 	value = phy_read(phydev, AT803X_INTR_ENABLE);
127 	if (value < 0)
128 		return;
129 
130 	if (value & AT803X_INTR_ENABLE_WOL)
131 		wol->wolopts |= WAKE_MAGIC;
132 }
133 EXPORT_SYMBOL_GPL(at803x_get_wol);
134 
135 int at803x_ack_interrupt(struct phy_device *phydev)
136 {
137 	int err;
138 
139 	err = phy_read(phydev, AT803X_INTR_STATUS);
140 
141 	return (err < 0) ? err : 0;
142 }
143 EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
144 
145 int at803x_config_intr(struct phy_device *phydev)
146 {
147 	int err;
148 	int value;
149 
150 	value = phy_read(phydev, AT803X_INTR_ENABLE);
151 
152 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
153 		/* Clear any pending interrupts */
154 		err = at803x_ack_interrupt(phydev);
155 		if (err)
156 			return err;
157 
158 		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
159 		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
160 		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
161 		value |= AT803X_INTR_ENABLE_LINK_FAIL;
162 		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
163 
164 		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
165 	} else {
166 		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
167 		if (err)
168 			return err;
169 
170 		/* Clear any pending interrupts */
171 		err = at803x_ack_interrupt(phydev);
172 	}
173 
174 	return err;
175 }
176 EXPORT_SYMBOL_GPL(at803x_config_intr);
177 
178 irqreturn_t at803x_handle_interrupt(struct phy_device *phydev)
179 {
180 	int irq_status, int_enabled;
181 
182 	irq_status = phy_read(phydev, AT803X_INTR_STATUS);
183 	if (irq_status < 0) {
184 		phy_error(phydev);
185 		return IRQ_NONE;
186 	}
187 
188 	/* Read the current enabled interrupts */
189 	int_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
190 	if (int_enabled < 0) {
191 		phy_error(phydev);
192 		return IRQ_NONE;
193 	}
194 
195 	/* See if this was one of our enabled interrupts */
196 	if (!(irq_status & int_enabled))
197 		return IRQ_NONE;
198 
199 	phy_trigger_machine(phydev);
200 
201 	return IRQ_HANDLED;
202 }
203 EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
204 
205 int at803x_read_specific_status(struct phy_device *phydev,
206 				struct at803x_ss_mask ss_mask)
207 {
208 	int ss;
209 
210 	/* Read the AT8035 PHY-Specific Status register, which indicates the
211 	 * speed and duplex that the PHY is actually using, irrespective of
212 	 * whether we are in autoneg mode or not.
213 	 */
214 	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
215 	if (ss < 0)
216 		return ss;
217 
218 	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
219 		int sfc, speed;
220 
221 		sfc = phy_read(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL);
222 		if (sfc < 0)
223 			return sfc;
224 
225 		speed = ss & ss_mask.speed_mask;
226 		speed >>= ss_mask.speed_shift;
227 
228 		switch (speed) {
229 		case AT803X_SS_SPEED_10:
230 			phydev->speed = SPEED_10;
231 			break;
232 		case AT803X_SS_SPEED_100:
233 			phydev->speed = SPEED_100;
234 			break;
235 		case AT803X_SS_SPEED_1000:
236 			phydev->speed = SPEED_1000;
237 			break;
238 		case QCA808X_SS_SPEED_2500:
239 			phydev->speed = SPEED_2500;
240 			break;
241 		}
242 		if (ss & AT803X_SS_DUPLEX)
243 			phydev->duplex = DUPLEX_FULL;
244 		else
245 			phydev->duplex = DUPLEX_HALF;
246 
247 		if (ss & AT803X_SS_MDIX)
248 			phydev->mdix = ETH_TP_MDI_X;
249 		else
250 			phydev->mdix = ETH_TP_MDI;
251 
252 		switch (FIELD_GET(AT803X_SFC_MDI_CROSSOVER_MODE_M, sfc)) {
253 		case AT803X_SFC_MANUAL_MDI:
254 			phydev->mdix_ctrl = ETH_TP_MDI;
255 			break;
256 		case AT803X_SFC_MANUAL_MDIX:
257 			phydev->mdix_ctrl = ETH_TP_MDI_X;
258 			break;
259 		case AT803X_SFC_AUTOMATIC_CROSSOVER:
260 			phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
261 			break;
262 		}
263 	}
264 
265 	return 0;
266 }
267 EXPORT_SYMBOL_GPL(at803x_read_specific_status);
268 
269 int at803x_config_mdix(struct phy_device *phydev, u8 ctrl)
270 {
271 	u16 val;
272 
273 	switch (ctrl) {
274 	case ETH_TP_MDI:
275 		val = AT803X_SFC_MANUAL_MDI;
276 		break;
277 	case ETH_TP_MDI_X:
278 		val = AT803X_SFC_MANUAL_MDIX;
279 		break;
280 	case ETH_TP_MDI_AUTO:
281 		val = AT803X_SFC_AUTOMATIC_CROSSOVER;
282 		break;
283 	default:
284 		return 0;
285 	}
286 
287 	return phy_modify_changed(phydev, AT803X_SPECIFIC_FUNCTION_CONTROL,
288 			  AT803X_SFC_MDI_CROSSOVER_MODE_M,
289 			  FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
290 }
291 EXPORT_SYMBOL_GPL(at803x_config_mdix);
292 
293 int at803x_prepare_config_aneg(struct phy_device *phydev)
294 {
295 	int ret;
296 
297 	ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
298 	if (ret < 0)
299 		return ret;
300 
301 	/* Changes of the midx bits are disruptive to the normal operation;
302 	 * therefore any changes to these registers must be followed by a
303 	 * software reset to take effect.
304 	 */
305 	if (ret == 1) {
306 		ret = genphy_soft_reset(phydev);
307 		if (ret < 0)
308 			return ret;
309 	}
310 
311 	return 0;
312 }
313 EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
314 
315 int at803x_read_status(struct phy_device *phydev)
316 {
317 	struct at803x_ss_mask ss_mask = { 0 };
318 	int err, old_link = phydev->link;
319 
320 	/* Update the link, but return if there was an error */
321 	err = genphy_update_link(phydev);
322 	if (err)
323 		return err;
324 
325 	/* why bother the PHY if nothing can have changed */
326 	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
327 		return 0;
328 
329 	phydev->speed = SPEED_UNKNOWN;
330 	phydev->duplex = DUPLEX_UNKNOWN;
331 	phydev->pause = 0;
332 	phydev->asym_pause = 0;
333 
334 	err = genphy_read_lpa(phydev);
335 	if (err < 0)
336 		return err;
337 
338 	ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
339 	ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
340 	err = at803x_read_specific_status(phydev, ss_mask);
341 	if (err < 0)
342 		return err;
343 
344 	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
345 		phy_resolve_aneg_pause(phydev);
346 
347 	return 0;
348 }
349 EXPORT_SYMBOL_GPL(at803x_read_status);
350 
351 static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
352 {
353 	int val;
354 
355 	val = phy_read(phydev, AT803X_SMART_SPEED);
356 	if (val < 0)
357 		return val;
358 
359 	if (val & AT803X_SMART_SPEED_ENABLE)
360 		*d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
361 	else
362 		*d = DOWNSHIFT_DEV_DISABLE;
363 
364 	return 0;
365 }
366 
367 static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
368 {
369 	u16 mask, set;
370 	int ret;
371 
372 	switch (cnt) {
373 	case DOWNSHIFT_DEV_DEFAULT_COUNT:
374 		cnt = AT803X_DEFAULT_DOWNSHIFT;
375 		fallthrough;
376 	case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
377 		set = AT803X_SMART_SPEED_ENABLE |
378 		      AT803X_SMART_SPEED_BYPASS_TIMER |
379 		      FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
380 		mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
381 		break;
382 	case DOWNSHIFT_DEV_DISABLE:
383 		set = 0;
384 		mask = AT803X_SMART_SPEED_ENABLE |
385 		       AT803X_SMART_SPEED_BYPASS_TIMER;
386 		break;
387 	default:
388 		return -EINVAL;
389 	}
390 
391 	ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
392 
393 	/* After changing the smart speed settings, we need to perform a
394 	 * software reset, use phy_init_hw() to make sure we set the
395 	 * reapply any values which might got lost during software reset.
396 	 */
397 	if (ret == 1)
398 		ret = phy_init_hw(phydev);
399 
400 	return ret;
401 }
402 
403 int at803x_get_tunable(struct phy_device *phydev,
404 		       struct ethtool_tunable *tuna, void *data)
405 {
406 	switch (tuna->id) {
407 	case ETHTOOL_PHY_DOWNSHIFT:
408 		return at803x_get_downshift(phydev, data);
409 	default:
410 		return -EOPNOTSUPP;
411 	}
412 }
413 EXPORT_SYMBOL_GPL(at803x_get_tunable);
414 
415 int at803x_set_tunable(struct phy_device *phydev,
416 		       struct ethtool_tunable *tuna, const void *data)
417 {
418 	switch (tuna->id) {
419 	case ETHTOOL_PHY_DOWNSHIFT:
420 		return at803x_set_downshift(phydev, *(const u8 *)data);
421 	default:
422 		return -EOPNOTSUPP;
423 	}
424 }
425 EXPORT_SYMBOL_GPL(at803x_set_tunable);
426 
427 int at803x_cdt_fault_length(int dt)
428 {
429 	/* According to the datasheet the distance to the fault is
430 	 * DELTA_TIME * 0.824 meters.
431 	 *
432 	 * The author suspect the correct formula is:
433 	 *
434 	 *   fault_distance = DELTA_TIME * (c * VF) / 125MHz / 2
435 	 *
436 	 * where c is the speed of light, VF is the velocity factor of
437 	 * the twisted pair cable, 125MHz the counter frequency and
438 	 * we need to divide by 2 because the hardware will measure the
439 	 * round trip time to the fault and back to the PHY.
440 	 *
441 	 * With a VF of 0.69 we get the factor 0.824 mentioned in the
442 	 * datasheet.
443 	 */
444 	return (dt * 824) / 10;
445 }
446 EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
447 
448 int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
449 {
450 	return phy_write(phydev, AT803X_CDT, cdt_start);
451 }
452 EXPORT_SYMBOL_GPL(at803x_cdt_start);
453 
454 int at803x_cdt_wait_for_completion(struct phy_device *phydev,
455 				   u32 cdt_en)
456 {
457 	int val, ret;
458 
459 	/* One test run takes about 25ms */
460 	ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
461 				    !(val & cdt_en),
462 				    30000, 100000, true);
463 
464 	return ret < 0 ? ret : 0;
465 }
466 EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
467 
468 static bool qca808x_cdt_fault_length_valid(int cdt_code)
469 {
470 	switch (cdt_code) {
471 	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
472 	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
473 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
474 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
475 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
476 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
477 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
478 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
479 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
480 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
481 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
482 		return true;
483 	default:
484 		return false;
485 	}
486 }
487 
488 static int qca808x_cable_test_result_trans(int cdt_code)
489 {
490 	switch (cdt_code) {
491 	case QCA808X_CDT_STATUS_STAT_NORMAL:
492 		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
493 	case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
494 		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
495 	case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
496 		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
497 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
498 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
499 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
500 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
501 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
502 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
503 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
504 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
505 	case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
506 		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
507 	case QCA808X_CDT_STATUS_STAT_FAIL:
508 	default:
509 		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
510 	}
511 }
512 
513 static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
514 				    int result)
515 {
516 	int val;
517 	u32 cdt_length_reg = 0;
518 
519 	switch (pair) {
520 	case ETHTOOL_A_CABLE_PAIR_A:
521 		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
522 		break;
523 	case ETHTOOL_A_CABLE_PAIR_B:
524 		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
525 		break;
526 	case ETHTOOL_A_CABLE_PAIR_C:
527 		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
528 		break;
529 	case ETHTOOL_A_CABLE_PAIR_D:
530 		cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
531 		break;
532 	default:
533 		return -EINVAL;
534 	}
535 
536 	val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
537 	if (val < 0)
538 		return val;
539 
540 	if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
541 		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
542 	else
543 		val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
544 
545 	return at803x_cdt_fault_length(val);
546 }
547 
548 static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
549 					      u16 status)
550 {
551 	int length, result;
552 	u16 pair_code;
553 
554 	switch (pair) {
555 	case ETHTOOL_A_CABLE_PAIR_A:
556 		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
557 		break;
558 	case ETHTOOL_A_CABLE_PAIR_B:
559 		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
560 		break;
561 	case ETHTOOL_A_CABLE_PAIR_C:
562 		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
563 		break;
564 	case ETHTOOL_A_CABLE_PAIR_D:
565 		pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
566 		break;
567 	default:
568 		return -EINVAL;
569 	}
570 
571 	result = qca808x_cable_test_result_trans(pair_code);
572 	ethnl_cable_test_result(phydev, pair, result);
573 
574 	if (qca808x_cdt_fault_length_valid(pair_code)) {
575 		length = qca808x_cdt_fault_length(phydev, pair, result);
576 		ethnl_cable_test_fault_length(phydev, pair, length);
577 	}
578 
579 	return 0;
580 }
581 
582 int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
583 {
584 	int ret, val;
585 
586 	*finished = false;
587 
588 	val = QCA808X_CDT_ENABLE_TEST |
589 	      QCA808X_CDT_LENGTH_UNIT;
590 	ret = at803x_cdt_start(phydev, val);
591 	if (ret)
592 		return ret;
593 
594 	ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
595 	if (ret)
596 		return ret;
597 
598 	val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
599 	if (val < 0)
600 		return val;
601 
602 	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
603 	if (ret)
604 		return ret;
605 
606 	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
607 	if (ret)
608 		return ret;
609 
610 	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
611 	if (ret)
612 		return ret;
613 
614 	ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
615 	if (ret)
616 		return ret;
617 
618 	*finished = true;
619 
620 	return 0;
621 }
622 EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
623 
624 int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
625 {
626 	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
627 				  QCA808X_LED_FORCE_EN);
628 }
629 EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
630 
631 bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
632 {
633 	int val;
634 
635 	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
636 	return !(val & QCA808X_LED_FORCE_EN);
637 }
638 EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
639 
640 int qca808x_led_reg_brightness_set(struct phy_device *phydev,
641 				   u16 reg, enum led_brightness value)
642 {
643 	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
644 			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
645 			      QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
646 							      QCA808X_LED_FORCE_OFF));
647 }
648 EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
649 
650 int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
651 			      unsigned long *delay_on,
652 			      unsigned long *delay_off)
653 {
654 	int ret;
655 
656 	/* Set blink to 50% off, 50% on at 4Hz by default */
657 	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
658 			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
659 			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
660 	if (ret)
661 		return ret;
662 
663 	/* We use BLINK_1 for normal blinking */
664 	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
665 			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
666 			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
667 	if (ret)
668 		return ret;
669 
670 	/* We set blink to 4Hz, aka 250ms */
671 	*delay_on = 250 / 2;
672 	*delay_off = 250 / 2;
673 
674 	return 0;
675 }
676 EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
677