File Index
Files and directories
Sign in
FILE

qcom-phy-lib.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/phy/qcom/qcom-phy-lib.c C
  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
 12MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
 13MODULE_AUTHOR("Matus Ujhelyi");
 14MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
 15MODULE_LICENSE("GPL");
 16
 17int 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}
 27EXPORT_SYMBOL_GPL(at803x_debug_reg_read);
 28
 29int 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}
 45EXPORT_SYMBOL_GPL(at803x_debug_reg_mask);
 46
 47int 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}
 57EXPORT_SYMBOL_GPL(at803x_debug_reg_write);
 58
 59int 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}
116EXPORT_SYMBOL_GPL(at803x_set_wol);
117
118int 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}
141EXPORT_SYMBOL_GPL(at8031_set_wol);
142
143void 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}
158EXPORT_SYMBOL_GPL(at803x_get_wol);
159
160int 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}
168EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
169
170int 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}
201EXPORT_SYMBOL_GPL(at803x_config_intr);
202
203irqreturn_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}
228EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
229
230int 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}
292EXPORT_SYMBOL_GPL(at803x_read_specific_status);
293
294int 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}
316EXPORT_SYMBOL_GPL(at803x_config_mdix);
317
318int 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}
338EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
339
340int 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}
374EXPORT_SYMBOL_GPL(at803x_read_status);
375
376static 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
392static 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
428int 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}
438EXPORT_SYMBOL_GPL(at803x_get_tunable);
439
440int 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}
450EXPORT_SYMBOL_GPL(at803x_set_tunable);
451
452int 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}
471EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
472
473int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
474{
475	return phy_write(phydev, AT803X_CDT, cdt_start);
476}
477EXPORT_SYMBOL_GPL(at803x_cdt_start);
478
479int 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}
491EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
492
493static 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
513static 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
538static 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
573static 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
607int 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}
647EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
648
649int 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}
654EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
655
656bool 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}
663EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
664
665int 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}
673EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
674
675int 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}
701EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
702
703/* Enable CRC checking for both received and transmitted frames to ensure
704 * accurate counter recording. The hardware supports a 32-bit counter,
705 * configure the counter to clear after it is read to facilitate the
706 * implementation of a 64-bit software counter
707 */
708int qcom_phy_counter_config(struct phy_device *phydev)
709{
710	return phy_set_bits_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_CTRL,
711				QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN |
712				QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN);
713}
714EXPORT_SYMBOL_GPL(qcom_phy_counter_config);
715
716int qcom_phy_update_stats(struct phy_device *phydev,
717			  struct qcom_phy_hw_stats *hw_stats)
718{
719	int ret;
720	u32 cnt;
721
722	/* PHY 32-bit counter for RX packets. */
723	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_15_0);
724	if (ret < 0)
725		return ret;
726
727	cnt = ret;
728
729	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_31_16);
730	if (ret < 0)
731		return ret;
732
733	cnt |= ret << 16;
734	hw_stats->rx_pkts += cnt;
735
736	/* PHY 16-bit counter for RX CRC error packets. */
737	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_ERR_PKT);
738	if (ret < 0)
739		return ret;
740
741	hw_stats->rx_err_pkts += ret;
742
743	/* PHY 32-bit counter for TX packets. */
744	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_15_0);
745	if (ret < 0)
746		return ret;
747
748	cnt = ret;
749
750	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_31_16);
751	if (ret < 0)
752		return ret;
753
754	cnt |= ret << 16;
755	hw_stats->tx_pkts += cnt;
756
757	/* PHY 16-bit counter for TX CRC error packets. */
758	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_ERR_PKT);
759	if (ret < 0)
760		return ret;
761
762	hw_stats->tx_err_pkts += ret;
763
764	return 0;
765}
766EXPORT_SYMBOL_GPL(qcom_phy_update_stats);
767
768void qcom_phy_get_stats(struct ethtool_phy_stats *stats,
769			struct qcom_phy_hw_stats hw_stats)
770{
771	stats->tx_packets = hw_stats.tx_pkts;
772	stats->tx_errors = hw_stats.tx_err_pkts;
773	stats->rx_packets = hw_stats.rx_pkts;
774	stats->rx_errors = hw_stats.rx_err_pkts;
775}
776EXPORT_SYMBOL_GPL(qcom_phy_get_stats);
~/ Read-only file preview.