hc
2024-05-14 bedbef8ad3e75a304af6361af235302bcc61d06b
kernel/drivers/net/phy/bcm-phy-lib.c
....@@ -1,55 +1,105 @@
1
+// SPDX-License-Identifier: GPL-2.0
12 /*
23 * Copyright (C) 2015-2017 Broadcom
3
- *
4
- * This program is free software; you can redistribute it and/or
5
- * modify it under the terms of the GNU General Public License as
6
- * published by the Free Software Foundation version 2.
7
- *
8
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9
- * kind, whether express or implied; without even the implied warranty
10
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- * GNU General Public License for more details.
124 */
135
146 #include "bcm-phy-lib.h"
7
+#include <linux/bitfield.h>
158 #include <linux/brcmphy.h>
169 #include <linux/export.h>
1710 #include <linux/mdio.h>
1811 #include <linux/module.h>
1912 #include <linux/phy.h>
2013 #include <linux/ethtool.h>
14
+#include <linux/ethtool_netlink.h>
2115
2216 #define MII_BCM_CHANNEL_WIDTH 0x2000
2317 #define BCM_CL45VEN_EEE_ADV 0x3c
18
+
19
+int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
20
+{
21
+ int rc;
22
+
23
+ rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
24
+ if (rc < 0)
25
+ return rc;
26
+
27
+ return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
28
+}
29
+EXPORT_SYMBOL_GPL(__bcm_phy_write_exp);
2430
2531 int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
2632 {
2733 int rc;
2834
29
- rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
30
- if (rc < 0)
31
- return rc;
35
+ phy_lock_mdio_bus(phydev);
36
+ rc = __bcm_phy_write_exp(phydev, reg, val);
37
+ phy_unlock_mdio_bus(phydev);
3238
33
- return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
39
+ return rc;
3440 }
3541 EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
3642
37
-int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
43
+int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
3844 {
3945 int val;
4046
41
- val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
47
+ val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
4248 if (val < 0)
4349 return val;
4450
45
- val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
51
+ val = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
4652
4753 /* Restore default value. It's O.K. if this write fails. */
48
- phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
54
+ __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
4955
5056 return val;
5157 }
58
+EXPORT_SYMBOL_GPL(__bcm_phy_read_exp);
59
+
60
+int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
61
+{
62
+ int rc;
63
+
64
+ phy_lock_mdio_bus(phydev);
65
+ rc = __bcm_phy_read_exp(phydev, reg);
66
+ phy_unlock_mdio_bus(phydev);
67
+
68
+ return rc;
69
+}
5270 EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
71
+
72
+int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
73
+{
74
+ int new, ret;
75
+
76
+ ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
77
+ if (ret < 0)
78
+ return ret;
79
+
80
+ ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
81
+ if (ret < 0)
82
+ return ret;
83
+
84
+ new = (ret & ~mask) | set;
85
+ if (new == ret)
86
+ return 0;
87
+
88
+ return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new);
89
+}
90
+EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp);
91
+
92
+int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
93
+{
94
+ int ret;
95
+
96
+ phy_lock_mdio_bus(phydev);
97
+ ret = __bcm_phy_modify_exp(phydev, reg, mask, set);
98
+ phy_unlock_mdio_bus(phydev);
99
+
100
+ return ret;
101
+}
102
+EXPORT_SYMBOL_GPL(bcm_phy_modify_exp);
53103
54104 int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
55105 {
....@@ -163,6 +213,86 @@
163213 }
164214 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
165215
216
+int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
217
+{
218
+ int val;
219
+
220
+ val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
221
+ if (val < 0)
222
+ return val;
223
+
224
+ return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
225
+}
226
+EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
227
+
228
+int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
229
+{
230
+ int ret;
231
+
232
+ phy_lock_mdio_bus(phydev);
233
+ ret = __bcm_phy_read_rdb(phydev, rdb);
234
+ phy_unlock_mdio_bus(phydev);
235
+
236
+ return ret;
237
+}
238
+EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
239
+
240
+int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
241
+{
242
+ int ret;
243
+
244
+ ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
245
+ if (ret < 0)
246
+ return ret;
247
+
248
+ return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
249
+}
250
+EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
251
+
252
+int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
253
+{
254
+ int ret;
255
+
256
+ phy_lock_mdio_bus(phydev);
257
+ ret = __bcm_phy_write_rdb(phydev, rdb, val);
258
+ phy_unlock_mdio_bus(phydev);
259
+
260
+ return ret;
261
+}
262
+EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
263
+
264
+int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
265
+{
266
+ int new, ret;
267
+
268
+ ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
269
+ if (ret < 0)
270
+ return ret;
271
+
272
+ ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
273
+ if (ret < 0)
274
+ return ret;
275
+
276
+ new = (ret & ~mask) | set;
277
+ if (new == ret)
278
+ return 0;
279
+
280
+ return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
281
+}
282
+EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
283
+
284
+int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
285
+{
286
+ int ret;
287
+
288
+ phy_lock_mdio_bus(phydev);
289
+ ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
290
+ phy_unlock_mdio_bus(phydev);
291
+
292
+ return ret;
293
+}
294
+EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
295
+
166296 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
167297 {
168298 int val;
....@@ -217,9 +347,11 @@
217347 if (val < 0)
218348 return val;
219349
220
- if (phydev->supported & SUPPORTED_1000baseT_Full)
350
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
351
+ phydev->supported))
221352 mask |= MDIO_EEE_1000T;
222
- if (phydev->supported & SUPPORTED_100baseT_Full)
353
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
354
+ phydev->supported))
223355 mask |= MDIO_EEE_100TX;
224356
225357 if (enable)
....@@ -384,6 +516,265 @@
384516 }
385517 EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
386518
519
+void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
520
+{
521
+ /* Reset R_CAL/RC_CAL Engine */
522
+ bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
523
+
524
+ /* Disable Reset R_AL/RC_CAL Engine */
525
+ bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
526
+}
527
+EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
528
+
529
+int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
530
+{
531
+ /* Increase VCO range to prevent unlocking problem of PLL at low
532
+ * temp
533
+ */
534
+ bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
535
+
536
+ /* Change Ki to 011 */
537
+ bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
538
+
539
+ /* Disable loading of TVCO buffer to bandgap, set bandgap trim
540
+ * to 111
541
+ */
542
+ bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
543
+
544
+ /* Adjust bias current trim by -3 */
545
+ bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
546
+
547
+ /* Switch to CORE_BASE1E */
548
+ phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
549
+
550
+ bcm_phy_r_rc_cal_reset(phydev);
551
+
552
+ /* write AFE_RXCONFIG_0 */
553
+ bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
554
+
555
+ /* write AFE_RXCONFIG_1 */
556
+ bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
557
+
558
+ /* write AFE_RX_LP_COUNTER */
559
+ bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
560
+
561
+ /* write AFE_HPF_TRIM_OTHERS */
562
+ bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
563
+
564
+ /* write AFTE_TX_CONFIG */
565
+ bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
566
+
567
+ return 0;
568
+}
569
+EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
570
+
571
+int bcm_phy_enable_jumbo(struct phy_device *phydev)
572
+{
573
+ int ret;
574
+
575
+ ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);
576
+ if (ret < 0)
577
+ return ret;
578
+
579
+ /* Enable extended length packet reception */
580
+ ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
581
+ ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);
582
+ if (ret < 0)
583
+ return ret;
584
+
585
+ /* Enable the elastic FIFO for raising the transmission limit from
586
+ * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation
587
+ * latency.
588
+ */
589
+ return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);
590
+}
591
+EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);
592
+
593
+static int __bcm_phy_enable_rdb_access(struct phy_device *phydev)
594
+{
595
+ return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0);
596
+}
597
+
598
+static int __bcm_phy_enable_legacy_access(struct phy_device *phydev)
599
+{
600
+ return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087,
601
+ BCM54XX_ACCESS_MODE_LEGACY_EN);
602
+}
603
+
604
+static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb)
605
+{
606
+ u16 mask, set;
607
+ int ret;
608
+
609
+ /* Auto-negotiation must be enabled for cable diagnostics to work, but
610
+ * don't advertise any capabilities.
611
+ */
612
+ phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
613
+ phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
614
+ phy_write(phydev, MII_CTRL1000, 0);
615
+
616
+ phy_lock_mdio_bus(phydev);
617
+ if (is_rdb) {
618
+ ret = __bcm_phy_enable_legacy_access(phydev);
619
+ if (ret)
620
+ goto out;
621
+ }
622
+
623
+ mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK;
624
+ set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK |
625
+ FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK,
626
+ BCM54XX_ECD_CTRL_UNIT_CM);
627
+
628
+ ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set);
629
+
630
+out:
631
+ /* re-enable the RDB access even if there was an error */
632
+ if (is_rdb)
633
+ ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
634
+
635
+ phy_unlock_mdio_bus(phydev);
636
+
637
+ return ret;
638
+}
639
+
640
+static int bcm_phy_cable_test_report_trans(int result)
641
+{
642
+ switch (result) {
643
+ case BCM54XX_ECD_FAULT_TYPE_OK:
644
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
645
+ case BCM54XX_ECD_FAULT_TYPE_OPEN:
646
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
647
+ case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
648
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
649
+ case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
650
+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
651
+ case BCM54XX_ECD_FAULT_TYPE_INVALID:
652
+ case BCM54XX_ECD_FAULT_TYPE_BUSY:
653
+ default:
654
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
655
+ }
656
+}
657
+
658
+static bool bcm_phy_distance_valid(int result)
659
+{
660
+ switch (result) {
661
+ case BCM54XX_ECD_FAULT_TYPE_OPEN:
662
+ case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
663
+ case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
664
+ return true;
665
+ }
666
+ return false;
667
+}
668
+
669
+static int bcm_phy_report_length(struct phy_device *phydev, int pair)
670
+{
671
+ int val;
672
+
673
+ val = __bcm_phy_read_exp(phydev,
674
+ BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair);
675
+ if (val < 0)
676
+ return val;
677
+
678
+ if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID)
679
+ return 0;
680
+
681
+ ethnl_cable_test_fault_length(phydev, pair, val);
682
+
683
+ return 0;
684
+}
685
+
686
+static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
687
+ bool *finished, bool is_rdb)
688
+{
689
+ int pair_a, pair_b, pair_c, pair_d, ret;
690
+
691
+ *finished = false;
692
+
693
+ phy_lock_mdio_bus(phydev);
694
+
695
+ if (is_rdb) {
696
+ ret = __bcm_phy_enable_legacy_access(phydev);
697
+ if (ret)
698
+ goto out;
699
+ }
700
+
701
+ ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL);
702
+ if (ret < 0)
703
+ goto out;
704
+
705
+ if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) {
706
+ ret = 0;
707
+ goto out;
708
+ }
709
+
710
+ ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE);
711
+ if (ret < 0)
712
+ goto out;
713
+
714
+ pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret);
715
+ pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret);
716
+ pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret);
717
+ pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret);
718
+
719
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
720
+ bcm_phy_cable_test_report_trans(pair_a));
721
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
722
+ bcm_phy_cable_test_report_trans(pair_b));
723
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
724
+ bcm_phy_cable_test_report_trans(pair_c));
725
+ ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
726
+ bcm_phy_cable_test_report_trans(pair_d));
727
+
728
+ if (bcm_phy_distance_valid(pair_a))
729
+ bcm_phy_report_length(phydev, 0);
730
+ if (bcm_phy_distance_valid(pair_b))
731
+ bcm_phy_report_length(phydev, 1);
732
+ if (bcm_phy_distance_valid(pair_c))
733
+ bcm_phy_report_length(phydev, 2);
734
+ if (bcm_phy_distance_valid(pair_d))
735
+ bcm_phy_report_length(phydev, 3);
736
+
737
+ ret = 0;
738
+ *finished = true;
739
+out:
740
+ /* re-enable the RDB access even if there was an error */
741
+ if (is_rdb)
742
+ ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
743
+
744
+ phy_unlock_mdio_bus(phydev);
745
+
746
+ return ret;
747
+}
748
+
749
+int bcm_phy_cable_test_start(struct phy_device *phydev)
750
+{
751
+ return _bcm_phy_cable_test_start(phydev, false);
752
+}
753
+EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start);
754
+
755
+int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished)
756
+{
757
+ return _bcm_phy_cable_test_get_status(phydev, finished, false);
758
+}
759
+EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status);
760
+
761
+/* We assume that all PHYs which support RDB access can be switched to legacy
762
+ * mode. If, in the future, this is not true anymore, we have to re-implement
763
+ * this with RDB access.
764
+ */
765
+int bcm_phy_cable_test_start_rdb(struct phy_device *phydev)
766
+{
767
+ return _bcm_phy_cable_test_start(phydev, true);
768
+}
769
+EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb);
770
+
771
+int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,
772
+ bool *finished)
773
+{
774
+ return _bcm_phy_cable_test_get_status(phydev, finished, true);
775
+}
776
+EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);
777
+
387778 MODULE_DESCRIPTION("Broadcom PHY Library");
388779 MODULE_LICENSE("GPL v2");
389780 MODULE_AUTHOR("Broadcom Corporation");