]> Git Repo - J-linux.git/blob - drivers/net/phy/qcom/qcom-phy-lib.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / net / phy / qcom / qcom-phy-lib.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
12 MODULE_DESCRIPTION("Qualcomm PHY driver Common Functions");
13 MODULE_AUTHOR("Matus Ujhelyi");
14 MODULE_AUTHOR("Christian Marangi <[email protected]>");
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);
This page took 0.070687 seconds and 4 git commands to generate.