1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2020 Sartura Ltd.
5 * Author: Robert Marko <robert.marko@sartura.hr>
7 * Qualcomm QCA8072 and QCA8075 PHY driver
10 #include <linux/version.h>
11 #include <linux/module.h>
13 #include <linux/phy.h>
14 #include <linux/bitfield.h>
15 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
16 #include <linux/ethtool_netlink.h>
18 #include <linux/gpio.h>
19 #include <linux/sfp.h>
21 #include <dt-bindings/net/qcom-qca807x.h>
23 #define PHY_ID_QCA8072 0x004dd0b2
24 #define PHY_ID_QCA8075 0x004dd0b1
25 #define PHY_ID_QCA807X_PSGMII 0x06820805
28 #define QCA807X_SMARTSPEED_EN BIT(5)
29 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
30 #define QCA807X_SMARTSPEED_RETRY_LIMIT_DEFAULT 5
31 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MIN 2
32 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MAX 9
34 /* Cable diagnostic test (CDT) */
35 #define QCA807X_CDT 0x16
36 #define QCA807X_CDT_ENABLE BIT(15)
37 #define QCA807X_CDT_ENABLE_INTER_PAIR_SHORT BIT(13)
38 #define QCA807X_CDT_STATUS BIT(11)
39 #define QCA807X_CDT_MMD3_STATUS 0x8064
40 #define QCA807X_CDT_MDI0_STATUS_MASK GENMASK(15, 12)
41 #define QCA807X_CDT_MDI1_STATUS_MASK GENMASK(11, 8)
42 #define QCA807X_CDT_MDI2_STATUS_MASK GENMASK(7, 4)
43 #define QCA807X_CDT_MDI3_STATUS_MASK GENMASK(3, 0)
44 #define QCA807X_CDT_RESULTS_INVALID 0x0
45 #define QCA807X_CDT_RESULTS_OK 0x1
46 #define QCA807X_CDT_RESULTS_OPEN 0x2
47 #define QCA807X_CDT_RESULTS_SAME_SHORT 0x3
48 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK 0x4
49 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK 0x8
50 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK 0xc
51 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN 0x6
52 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN 0xa
53 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN 0xe
54 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT 0x7
55 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT 0xb
56 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT 0xf
57 #define QCA807X_CDT_RESULTS_BUSY 0x9
58 #define QCA807X_CDT_MMD3_MDI0_LENGTH 0x8065
59 #define QCA807X_CDT_MMD3_MDI1_LENGTH 0x8066
60 #define QCA807X_CDT_MMD3_MDI2_LENGTH 0x8067
61 #define QCA807X_CDT_MMD3_MDI3_LENGTH 0x8068
62 #define QCA807X_CDT_SAME_SHORT_LENGTH_MASK GENMASK(15, 8)
63 #define QCA807X_CDT_CROSS_SHORT_LENGTH_MASK GENMASK(7, 0)
65 #define QCA807X_CHIP_CONFIGURATION 0x1f
66 #define QCA807X_BT_BX_REG_SEL BIT(15)
67 #define QCA807X_BT_BX_REG_SEL_FIBER 0
68 #define QCA807X_BT_BX_REG_SEL_COPPER 1
69 #define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)
70 #define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4
71 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3
72 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0
74 #define QCA807X_MEDIA_SELECT_STATUS 0x1a
75 #define QCA807X_MEDIA_DETECTED_COPPER BIT(5)
76 #define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)
77 #define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)
79 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
80 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
82 #define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
83 #define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
85 #define QCA807X_MMD7_LED_100N_1 0x8074
86 #define QCA807X_MMD7_LED_100N_2 0x8075
87 #define QCA807X_MMD7_LED_1000N_1 0x8076
88 #define QCA807X_MMD7_LED_1000N_2 0x8077
89 #define QCA807X_LED_TXACT_BLK_EN_2 BIT(10)
90 #define QCA807X_LED_RXACT_BLK_EN_2 BIT(9)
91 #define QCA807X_LED_GT_ON_EN_2 BIT(6)
92 #define QCA807X_LED_HT_ON_EN_2 BIT(5)
93 #define QCA807X_LED_BT_ON_EN_2 BIT(4)
94 #define QCA807X_GPIO_FORCE_EN BIT(15)
95 #define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
97 #define QCA807X_INTR_ENABLE 0x12
98 #define QCA807X_INTR_STATUS 0x13
99 #define QCA807X_INTR_ENABLE_AUTONEG_ERR BIT(15)
100 #define QCA807X_INTR_ENABLE_SPEED_CHANGED BIT(14)
101 #define QCA807X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
102 #define QCA807X_INTR_ENABLE_LINK_FAIL BIT(11)
103 #define QCA807X_INTR_ENABLE_LINK_SUCCESS BIT(10)
105 #define QCA807X_FUNCTION_CONTROL 0x10
106 #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
107 #define QCA807X_FC_MDI_CROSSOVER_AUTO 3
108 #define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1
109 #define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0
111 #define QCA807X_PHY_SPECIFIC_STATUS 0x11
112 #define QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED BIT(11)
113 #define QCA807X_SS_SPEED_MASK GENMASK(15, 14)
114 #define QCA807X_SS_SPEED_1000 2
115 #define QCA807X_SS_SPEED_100 1
116 #define QCA807X_SS_SPEED_10 0
117 #define QCA807X_SS_DUPLEX BIT(13)
118 #define QCA807X_SS_MDIX BIT(6)
120 /* PSGMII PHY specific */
121 #define PSGMII_QSGMII_DRIVE_CONTROL_1 0xb
122 #define PSGMII_QSGMII_TX_DRIVER_MASK GENMASK(7, 4)
123 #define PSGMII_MODE_CTRL 0x6d
124 #define PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0)
125 #define PSGMII_MMD3_SERDES_CONTROL 0x805a
127 struct qca807x_gpio_priv
{
128 struct phy_device
*phy
;
131 static int qca807x_get_downshift(struct phy_device
*phydev
, u8
*data
)
133 int val
, cnt
, enable
;
135 val
= phy_read(phydev
, MII_NWAYTEST
);
139 enable
= FIELD_GET(QCA807X_SMARTSPEED_EN
, val
);
140 cnt
= FIELD_GET(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
, val
) + 2;
142 *data
= enable
? cnt
: DOWNSHIFT_DEV_DISABLE
;
147 static int qca807x_set_downshift(struct phy_device
*phydev
, u8 cnt
)
151 if (cnt
> QCA807X_SMARTSPEED_RETRY_LIMIT_MAX
||
152 (cnt
< QCA807X_SMARTSPEED_RETRY_LIMIT_MIN
&& cnt
!= DOWNSHIFT_DEV_DISABLE
))
156 ret
= phy_clear_bits(phydev
, MII_NWAYTEST
, QCA807X_SMARTSPEED_EN
);
158 val
= QCA807X_SMARTSPEED_EN
;
159 val
|= FIELD_PREP(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
, cnt
- 2);
161 phy_modify(phydev
, MII_NWAYTEST
,
162 QCA807X_SMARTSPEED_EN
|
163 QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
,
167 ret
= genphy_soft_reset(phydev
);
172 static int qca807x_get_tunable(struct phy_device
*phydev
,
173 struct ethtool_tunable
*tuna
, void *data
)
176 case ETHTOOL_PHY_DOWNSHIFT
:
177 return qca807x_get_downshift(phydev
, data
);
183 static int qca807x_set_tunable(struct phy_device
*phydev
,
184 struct ethtool_tunable
*tuna
, const void *data
)
187 case ETHTOOL_PHY_DOWNSHIFT
:
188 return qca807x_set_downshift(phydev
, *(const u8
*)data
);
194 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
195 static bool qca807x_distance_valid(int result
)
198 case QCA807X_CDT_RESULTS_OPEN
:
199 case QCA807X_CDT_RESULTS_SAME_SHORT
:
200 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK
:
201 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK
:
202 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK
:
203 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN
:
204 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN
:
205 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN
:
206 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT
:
207 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT
:
208 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT
:
214 static int qca807x_report_length(struct phy_device
*phydev
,
215 int pair
, int result
)
220 ret
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, QCA807X_CDT_MMD3_MDI0_LENGTH
+ pair
);
225 case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT
:
226 length
= (FIELD_GET(QCA807X_CDT_SAME_SHORT_LENGTH_MASK
, ret
) * 800) / 10;
228 case ETHTOOL_A_CABLE_RESULT_CODE_OPEN
:
229 case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT
:
230 length
= (FIELD_GET(QCA807X_CDT_CROSS_SHORT_LENGTH_MASK
, ret
) * 800) / 10;
234 ethnl_cable_test_fault_length(phydev
, pair
, length
);
239 static int qca807x_cable_test_report_trans(int result
)
242 case QCA807X_CDT_RESULTS_OK
:
243 return ETHTOOL_A_CABLE_RESULT_CODE_OK
;
244 case QCA807X_CDT_RESULTS_OPEN
:
245 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN
;
246 case QCA807X_CDT_RESULTS_SAME_SHORT
:
247 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT
;
248 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK
:
249 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK
:
250 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK
:
251 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN
:
252 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN
:
253 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN
:
254 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT
:
255 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT
:
256 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT
:
257 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT
;
259 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC
;
263 static int qca807x_cable_test_report(struct phy_device
*phydev
)
265 int pair0
, pair1
, pair2
, pair3
;
268 ret
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, QCA807X_CDT_MMD3_STATUS
);
272 pair0
= FIELD_GET(QCA807X_CDT_MDI0_STATUS_MASK
, ret
);
273 pair1
= FIELD_GET(QCA807X_CDT_MDI1_STATUS_MASK
, ret
);
274 pair2
= FIELD_GET(QCA807X_CDT_MDI2_STATUS_MASK
, ret
);
275 pair3
= FIELD_GET(QCA807X_CDT_MDI3_STATUS_MASK
, ret
);
277 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_A
,
278 qca807x_cable_test_report_trans(pair0
));
279 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_B
,
280 qca807x_cable_test_report_trans(pair1
));
281 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_C
,
282 qca807x_cable_test_report_trans(pair2
));
283 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_D
,
284 qca807x_cable_test_report_trans(pair3
));
286 if (qca807x_distance_valid(pair0
))
287 qca807x_report_length(phydev
, 0, qca807x_cable_test_report_trans(pair0
));
288 if (qca807x_distance_valid(pair1
))
289 qca807x_report_length(phydev
, 1, qca807x_cable_test_report_trans(pair1
));
290 if (qca807x_distance_valid(pair2
))
291 qca807x_report_length(phydev
, 2, qca807x_cable_test_report_trans(pair2
));
292 if (qca807x_distance_valid(pair3
))
293 qca807x_report_length(phydev
, 3, qca807x_cable_test_report_trans(pair3
));
298 static int qca807x_cable_test_get_status(struct phy_device
*phydev
,
305 val
= phy_read(phydev
, QCA807X_CDT
);
306 if (!((val
& QCA807X_CDT_ENABLE
) && (val
& QCA807X_CDT_STATUS
))) {
309 return qca807x_cable_test_report(phydev
);
315 static int qca807x_cable_test_start(struct phy_device
*phydev
)
319 val
= phy_read(phydev
, QCA807X_CDT
);
320 /* Enable inter-pair short check as well */
321 val
&= ~QCA807X_CDT_ENABLE_INTER_PAIR_SHORT
;
322 val
|= QCA807X_CDT_ENABLE
;
323 ret
= phy_write(phydev
, QCA807X_CDT
, val
);
329 #ifdef CONFIG_GPIOLIB
330 static int qca807x_gpio_get_direction(struct gpio_chip
*gc
, unsigned int offset
)
332 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
333 return GPIO_LINE_DIRECTION_OUT
;
335 return GPIOF_DIR_OUT
;
339 static int qca807x_gpio_get_reg(unsigned int offset
)
341 return QCA807X_MMD7_LED_100N_2
+ (offset
% 2) * 2;
344 static int qca807x_gpio_get(struct gpio_chip
*gc
, unsigned int offset
)
346 struct qca807x_gpio_priv
*priv
= gpiochip_get_data(gc
);
349 val
= phy_read_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
));
351 return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK
, val
);
354 static void qca807x_gpio_set(struct gpio_chip
*gc
, unsigned int offset
, int value
)
356 struct qca807x_gpio_priv
*priv
= gpiochip_get_data(gc
);
359 val
= phy_read_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
));
360 val
&= ~QCA807X_GPIO_FORCE_MODE_MASK
;
361 val
|= QCA807X_GPIO_FORCE_EN
;
362 val
|= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK
, value
);
364 phy_write_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
), val
);
367 static int qca807x_gpio_dir_out(struct gpio_chip
*gc
, unsigned int offset
, int value
)
369 qca807x_gpio_set(gc
, offset
, value
);
374 static int qca807x_gpio(struct phy_device
*phydev
)
376 struct device
*dev
= &phydev
->mdio
.dev
;
377 struct qca807x_gpio_priv
*priv
;
378 struct gpio_chip
*gc
;
380 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
386 gc
= devm_kzalloc(dev
, sizeof(*gc
), GFP_KERNEL
);
390 gc
->label
= dev_name(dev
);
394 gc
->owner
= THIS_MODULE
;
395 gc
->can_sleep
= true;
396 gc
->get_direction
= qca807x_gpio_get_direction
;
397 gc
->direction_output
= qca807x_gpio_dir_out
;
398 gc
->get
= qca807x_gpio_get
;
399 gc
->set
= qca807x_gpio_set
;
401 return devm_gpiochip_add_data(dev
, gc
, priv
);
405 static int qca807x_read_copper_status(struct phy_device
*phydev
)
407 int ss
, err
, old_link
= phydev
->link
;
409 /* Update the link, but return if there was an error */
410 err
= genphy_update_link(phydev
);
414 /* why bother the PHY if nothing can have changed */
415 if (phydev
->autoneg
== AUTONEG_ENABLE
&& old_link
&& phydev
->link
)
418 phydev
->speed
= SPEED_UNKNOWN
;
419 phydev
->duplex
= DUPLEX_UNKNOWN
;
421 phydev
->asym_pause
= 0;
423 err
= genphy_read_lpa(phydev
);
427 /* Read the QCA807x PHY-Specific Status register copper page,
428 * which indicates the speed and duplex that the PHY is actually
429 * using, irrespective of whether we are in autoneg mode or not.
431 ss
= phy_read(phydev
, QCA807X_PHY_SPECIFIC_STATUS
);
435 if (ss
& QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED
) {
438 sfc
= phy_read(phydev
, QCA807X_FUNCTION_CONTROL
);
442 switch (FIELD_GET(QCA807X_SS_SPEED_MASK
, ss
)) {
443 case QCA807X_SS_SPEED_10
:
444 phydev
->speed
= SPEED_10
;
446 case QCA807X_SS_SPEED_100
:
447 phydev
->speed
= SPEED_100
;
449 case QCA807X_SS_SPEED_1000
:
450 phydev
->speed
= SPEED_1000
;
453 if (ss
& QCA807X_SS_DUPLEX
)
454 phydev
->duplex
= DUPLEX_FULL
;
456 phydev
->duplex
= DUPLEX_HALF
;
458 if (ss
& QCA807X_SS_MDIX
)
459 phydev
->mdix
= ETH_TP_MDI_X
;
461 phydev
->mdix
= ETH_TP_MDI
;
463 switch (FIELD_GET(QCA807X_FC_MDI_CROSSOVER_MODE_MASK
, sfc
)) {
464 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI
:
465 phydev
->mdix_ctrl
= ETH_TP_MDI
;
467 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX
:
468 phydev
->mdix_ctrl
= ETH_TP_MDI_X
;
470 case QCA807X_FC_MDI_CROSSOVER_AUTO
:
471 phydev
->mdix_ctrl
= ETH_TP_MDI_AUTO
;
476 if (phydev
->autoneg
== AUTONEG_ENABLE
&& phydev
->autoneg_complete
)
477 phy_resolve_aneg_pause(phydev
);
482 static int qca807x_read_fiber_status(struct phy_device
*phydev
)
484 int ss
, err
, lpa
, old_link
= phydev
->link
;
486 /* Update the link, but return if there was an error */
487 err
= genphy_update_link(phydev
);
491 /* why bother the PHY if nothing can have changed */
492 if (phydev
->autoneg
== AUTONEG_ENABLE
&& old_link
&& phydev
->link
)
495 phydev
->speed
= SPEED_UNKNOWN
;
496 phydev
->duplex
= DUPLEX_UNKNOWN
;
498 phydev
->asym_pause
= 0;
500 if (phydev
->autoneg
== AUTONEG_ENABLE
&& phydev
->autoneg_complete
) {
501 lpa
= phy_read(phydev
, MII_LPA
);
505 linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
,
506 phydev
->lp_advertising
, lpa
& LPA_LPACK
);
507 linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT
,
508 phydev
->lp_advertising
, lpa
& LPA_1000XFULL
);
509 linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT
,
510 phydev
->lp_advertising
, lpa
& LPA_1000XPAUSE
);
511 linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT
,
512 phydev
->lp_advertising
,
513 lpa
& LPA_1000XPAUSE_ASYM
);
515 phy_resolve_aneg_linkmode(phydev
);
518 /* Read the QCA807x PHY-Specific Status register fiber page,
519 * which indicates the speed and duplex that the PHY is actually
520 * using, irrespective of whether we are in autoneg mode or not.
522 ss
= phy_read(phydev
, QCA807X_PHY_SPECIFIC_STATUS
);
526 if (ss
& QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED
) {
527 switch (FIELD_GET(QCA807X_SS_SPEED_MASK
, ss
)) {
528 case QCA807X_SS_SPEED_100
:
529 phydev
->speed
= SPEED_100
;
531 case QCA807X_SS_SPEED_1000
:
532 phydev
->speed
= SPEED_1000
;
536 if (ss
& QCA807X_SS_DUPLEX
)
537 phydev
->duplex
= DUPLEX_FULL
;
539 phydev
->duplex
= DUPLEX_HALF
;
545 static int qca807x_read_status(struct phy_device
*phydev
)
547 if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->supported
)) {
548 switch (phydev
->port
) {
550 return qca807x_read_fiber_status(phydev
);
552 return qca807x_read_copper_status(phydev
);
557 return qca807x_read_copper_status(phydev
);
560 static int qca807x_config_intr(struct phy_device
*phydev
)
564 val
= phy_read(phydev
, QCA807X_INTR_ENABLE
);
566 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
567 /* Check for combo port as it has fewer interrupts */
568 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
569 val
|= QCA807X_INTR_ENABLE_SPEED_CHANGED
;
570 val
|= QCA807X_INTR_ENABLE_LINK_FAIL
;
571 val
|= QCA807X_INTR_ENABLE_LINK_SUCCESS
;
573 val
|= QCA807X_INTR_ENABLE_AUTONEG_ERR
;
574 val
|= QCA807X_INTR_ENABLE_SPEED_CHANGED
;
575 val
|= QCA807X_INTR_ENABLE_DUPLEX_CHANGED
;
576 val
|= QCA807X_INTR_ENABLE_LINK_FAIL
;
577 val
|= QCA807X_INTR_ENABLE_LINK_SUCCESS
;
579 ret
= phy_write(phydev
, QCA807X_INTR_ENABLE
, val
);
581 ret
= phy_write(phydev
, QCA807X_INTR_ENABLE
, 0);
587 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
588 static int qca807x_ack_intr(struct phy_device
*phydev
)
592 ret
= phy_read(phydev
, QCA807X_INTR_STATUS
);
594 return (ret
< 0) ? ret
: 0;
597 static irqreturn_t
qca807x_handle_interrupt(struct phy_device
*phydev
)
599 int irq_status
, int_enabled
;
601 irq_status
= phy_read(phydev
, QCA807X_INTR_STATUS
);
602 if (irq_status
< 0) {
607 /* Read the current enabled interrupts */
608 int_enabled
= phy_read(phydev
, QCA807X_INTR_ENABLE
);
609 if (int_enabled
< 0) {
614 /* See if this was one of our enabled interrupts */
615 if (!(irq_status
& int_enabled
))
618 phy_trigger_machine(phydev
);
624 static int qca807x_led_config(struct phy_device
*phydev
)
626 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
627 bool led_config
= false;
630 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, QCA807X_MMD7_LED_1000N_1
);
634 if (of_property_read_bool(node
, "qcom,single-led-1000")) {
635 val
|= QCA807X_LED_TXACT_BLK_EN_2
;
636 val
|= QCA807X_LED_RXACT_BLK_EN_2
;
637 val
|= QCA807X_LED_GT_ON_EN_2
;
642 if (of_property_read_bool(node
, "qcom,single-led-100")) {
643 val
|= QCA807X_LED_HT_ON_EN_2
;
648 if (of_property_read_bool(node
, "qcom,single-led-10")) {
649 val
|= QCA807X_LED_BT_ON_EN_2
;
655 return phy_write_mmd(phydev
, MDIO_MMD_AN
, QCA807X_MMD7_LED_1000N_1
, val
);
660 static int qca807x_sfp_insert(void *upstream
, const struct sfp_eeprom_id
*id
)
662 struct phy_device
*phydev
= upstream
;
663 __ETHTOOL_DECLARE_LINK_MODE_MASK(support
) = { 0, };
664 phy_interface_t iface
;
667 sfp_parse_support(phydev
->sfp_bus
, id
, support
);
668 iface
= sfp_select_interface(phydev
->sfp_bus
, support
);
670 dev_info(&phydev
->mdio
.dev
, "%s SFP module inserted\n", phy_modes(iface
));
673 case PHY_INTERFACE_MODE_1000BASEX
:
674 case PHY_INTERFACE_MODE_100BASEX
:
675 /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
676 ret
= phy_modify(phydev
,
677 QCA807X_CHIP_CONFIGURATION
,
678 QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK
,
679 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER
);
680 /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
681 ret
= phy_set_bits_mmd(phydev
,
683 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION
,
684 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN
);
685 /* Select fiber page */
686 ret
= phy_clear_bits(phydev
,
687 QCA807X_CHIP_CONFIGURATION
,
688 QCA807X_BT_BX_REG_SEL
);
690 phydev
->port
= PORT_FIBRE
;
693 dev_err(&phydev
->mdio
.dev
, "Incompatible SFP module inserted\n");
700 static void qca807x_sfp_remove(void *upstream
)
702 struct phy_device
*phydev
= upstream
;
704 /* Select copper page */
706 QCA807X_CHIP_CONFIGURATION
,
707 QCA807X_BT_BX_REG_SEL
);
709 phydev
->port
= PORT_TP
;
712 static const struct sfp_upstream_ops qca807x_sfp_ops
= {
713 .attach
= phy_sfp_attach
,
714 .detach
= phy_sfp_detach
,
715 .module_insert
= qca807x_sfp_insert
,
716 .module_remove
= qca807x_sfp_remove
,
719 static int qca807x_config(struct phy_device
*phydev
)
721 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
722 int control_dac
, ret
= 0;
725 /* Check for Combo port */
726 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
729 /* Prevent PSGMII going into hibernation via PSGMII self test */
730 psgmii_serdes
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, PSGMII_MMD3_SERDES_CONTROL
);
731 psgmii_serdes
&= ~BIT(1);
732 ret
= phy_write_mmd(phydev
, MDIO_MMD_PCS
,
733 PSGMII_MMD3_SERDES_CONTROL
,
737 if (!of_property_read_u32(node
, "qcom,control-dac", &of_control_dac
)) {
738 control_dac
= phy_read_mmd(phydev
, MDIO_MMD_AN
,
739 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH
);
740 control_dac
&= ~QCA807X_CONTROL_DAC_MASK
;
741 control_dac
|= FIELD_PREP(QCA807X_CONTROL_DAC_MASK
, of_control_dac
);
742 ret
= phy_write_mmd(phydev
, MDIO_MMD_AN
,
743 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH
,
747 /* Optionally configure LED-s */
748 if (IS_ENABLED(CONFIG_GPIOLIB
)) {
749 /* Check whether PHY-s pins are used as GPIO-s */
750 if (!of_property_read_bool(node
, "gpio-controller"))
751 ret
= qca807x_led_config(phydev
);
753 ret
= qca807x_led_config(phydev
);
759 static int qca807x_probe(struct phy_device
*phydev
)
761 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
764 if (IS_ENABLED(CONFIG_GPIOLIB
)) {
765 /* Do not register a GPIO controller unless flagged for it */
766 if (of_property_read_bool(node
, "gpio-controller"))
767 ret
= qca807x_gpio(phydev
);
770 /* Attach SFP bus on combo port*/
771 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
772 ret
= phy_sfp_probe(phydev
, &qca807x_sfp_ops
);
773 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->supported
);
774 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->advertising
);
780 static int qca807x_psgmii_config(struct phy_device
*phydev
)
782 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
784 u32 tx_driver_strength
;
786 /* Workaround to enable AZ transmitting ability */
787 ret
= phy_clear_bits_mmd(phydev
,
790 PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK
);
792 /* PSGMII/QSGMII TX amp set to DT defined value instead of default 600mV */
793 if (!of_property_read_u32(node
, "qcom,tx-driver-strength", &tx_driver_strength
)) {
794 tx_amp
= phy_read(phydev
, PSGMII_QSGMII_DRIVE_CONTROL_1
);
795 tx_amp
&= ~PSGMII_QSGMII_TX_DRIVER_MASK
;
796 tx_amp
|= FIELD_PREP(PSGMII_QSGMII_TX_DRIVER_MASK
, tx_driver_strength
);
797 ret
= phy_write(phydev
, PSGMII_QSGMII_DRIVE_CONTROL_1
, tx_amp
);
803 static struct phy_driver qca807x_drivers
[] = {
805 PHY_ID_MATCH_EXACT(PHY_ID_QCA8072
),
806 .name
= "Qualcomm QCA8072",
807 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
808 .flags
= PHY_POLL_CABLE_TEST
,
810 /* PHY_GBIT_FEATURES */
811 .probe
= qca807x_probe
,
812 .config_init
= qca807x_config
,
813 .read_status
= qca807x_read_status
,
814 .config_intr
= qca807x_config_intr
,
815 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
816 .ack_interrupt
= qca807x_ack_intr
,
818 .handle_interrupt
= qca807x_handle_interrupt
,
820 .soft_reset
= genphy_soft_reset
,
821 .get_tunable
= qca807x_get_tunable
,
822 .set_tunable
= qca807x_set_tunable
,
823 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
824 .cable_test_start
= qca807x_cable_test_start
,
825 .cable_test_get_status
= qca807x_cable_test_get_status
,
829 PHY_ID_MATCH_EXACT(PHY_ID_QCA8075
),
830 .name
= "Qualcomm QCA8075",
831 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
832 .flags
= PHY_POLL_CABLE_TEST
,
834 /* PHY_GBIT_FEATURES */
835 .probe
= qca807x_probe
,
836 .config_init
= qca807x_config
,
837 .read_status
= qca807x_read_status
,
838 .config_intr
= qca807x_config_intr
,
839 #if LINUX_VERSION_CODE < KERNEL_VERSION(5,12,0)
840 .ack_interrupt
= qca807x_ack_intr
,
842 .handle_interrupt
= qca807x_handle_interrupt
,
844 .soft_reset
= genphy_soft_reset
,
845 .get_tunable
= qca807x_get_tunable
,
846 .set_tunable
= qca807x_set_tunable
,
847 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
848 .cable_test_start
= qca807x_cable_test_start
,
849 .cable_test_get_status
= qca807x_cable_test_get_status
,
853 PHY_ID_MATCH_EXACT(PHY_ID_QCA807X_PSGMII
),
854 .name
= "Qualcomm QCA807x PSGMII",
855 .probe
= qca807x_psgmii_config
,
858 module_phy_driver(qca807x_drivers
);
860 static struct mdio_device_id __maybe_unused qca807x_tbl
[] = {
861 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072
) },
862 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075
) },
863 { PHY_ID_MATCH_MODEL(PHY_ID_QCA807X_PSGMII
) },
867 MODULE_AUTHOR("Robert Marko");
868 MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
869 MODULE_DEVICE_TABLE(mdio
, qca807x_tbl
);
870 MODULE_LICENSE("GPL");