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
118void 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}
133EXPORT_SYMBOL_GPL(at803x_get_wol);
134
135int 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}
143EXPORT_SYMBOL_GPL(at803x_ack_interrupt);
144
145int 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}
176EXPORT_SYMBOL_GPL(at803x_config_intr);
177
178irqreturn_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}
203EXPORT_SYMBOL_GPL(at803x_handle_interrupt);
204
205int 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}
267EXPORT_SYMBOL_GPL(at803x_read_specific_status);
268
269int 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}
291EXPORT_SYMBOL_GPL(at803x_config_mdix);
292
293int 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}
313EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
314
315int 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}
349EXPORT_SYMBOL_GPL(at803x_read_status);
350
351static 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
367static 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
403int 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}
413EXPORT_SYMBOL_GPL(at803x_get_tunable);
414
415int 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}
425EXPORT_SYMBOL_GPL(at803x_set_tunable);
426
427int 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}
446EXPORT_SYMBOL_GPL(at803x_cdt_fault_length);
447
448int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start)
449{
450	return phy_write(phydev, AT803X_CDT, cdt_start);
451}
452EXPORT_SYMBOL_GPL(at803x_cdt_start);
453
454int 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}
466EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
467
468static 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
488static 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
513static 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
548static 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
582int 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}
622EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
623
624int 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}
629EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
630
631bool 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}
638EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
639
640int 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}
648EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
649
650int 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}
676EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
677