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