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