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_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)
68 #define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4
69 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3
70 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0
72 #define QCA807X_MEDIA_SELECT_STATUS 0x1a
73 #define QCA807X_MEDIA_DETECTED_COPPER BIT(5)
74 #define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)
75 #define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)
77 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
78 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
80 #define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
81 #define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
83 #define QCA807X_MMD7_LED_100N_1 0x8074
84 #define QCA807X_MMD7_LED_100N_2 0x8075
85 #define QCA807X_MMD7_LED_1000N_1 0x8076
86 #define QCA807X_MMD7_LED_1000N_2 0x8077
87 #define QCA807X_LED_TXACT_BLK_EN_2 BIT(10)
88 #define QCA807X_LED_RXACT_BLK_EN_2 BIT(9)
89 #define QCA807X_LED_GT_ON_EN_2 BIT(6)
90 #define QCA807X_LED_HT_ON_EN_2 BIT(5)
91 #define QCA807X_LED_BT_ON_EN_2 BIT(4)
92 #define QCA807X_GPIO_FORCE_EN BIT(15)
93 #define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
95 #define QCA807X_INTR_ENABLE 0x12
96 #define QCA807X_INTR_STATUS 0x13
97 #define QCA807X_INTR_ENABLE_AUTONEG_ERR BIT(15)
98 #define QCA807X_INTR_ENABLE_SPEED_CHANGED BIT(14)
99 #define QCA807X_INTR_ENABLE_DUPLEX_CHANGED BIT(13)
100 #define QCA807X_INTR_ENABLE_LINK_FAIL BIT(11)
101 #define QCA807X_INTR_ENABLE_LINK_SUCCESS BIT(10)
103 #define QCA807X_FUNCTION_CONTROL 0x10
104 #define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
105 #define QCA807X_FC_MDI_CROSSOVER_AUTO 3
106 #define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1
107 #define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0
109 #define QCA807X_PHY_SPECIFIC_STATUS 0x11
110 #define QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED BIT(11)
111 #define QCA807X_SS_SPEED_MASK GENMASK(15, 14)
112 #define QCA807X_SS_SPEED_1000 2
113 #define QCA807X_SS_SPEED_100 1
114 #define QCA807X_SS_SPEED_10 0
115 #define QCA807X_SS_DUPLEX BIT(13)
116 #define QCA807X_SS_MDIX BIT(6)
118 /* PSGMII PHY specific */
119 #define PSGMII_QSGMII_DRIVE_CONTROL_1 0xb
120 #define PSGMII_QSGMII_TX_DRIVER_MASK GENMASK(7, 4)
121 #define PSGMII_MODE_CTRL 0x6d
122 #define PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK GENMASK(3, 0)
123 #define PSGMII_MMD3_SERDES_CONTROL 0x805a
125 struct qca807x_gpio_priv
{
126 struct phy_device
*phy
;
129 static int qca807x_get_downshift(struct phy_device
*phydev
, u8
*data
)
131 int val
, cnt
, enable
;
133 val
= phy_read(phydev
, MII_NWAYTEST
);
137 enable
= FIELD_GET(QCA807X_SMARTSPEED_EN
, val
);
138 cnt
= FIELD_GET(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
, val
) + 2;
140 *data
= enable
? cnt
: DOWNSHIFT_DEV_DISABLE
;
145 static int qca807x_set_downshift(struct phy_device
*phydev
, u8 cnt
)
149 if (cnt
> QCA807X_SMARTSPEED_RETRY_LIMIT_MAX
||
150 (cnt
< QCA807X_SMARTSPEED_RETRY_LIMIT_MIN
&& cnt
!= DOWNSHIFT_DEV_DISABLE
))
154 ret
= phy_clear_bits(phydev
, MII_NWAYTEST
, QCA807X_SMARTSPEED_EN
);
156 val
= QCA807X_SMARTSPEED_EN
;
157 val
|= FIELD_PREP(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
, cnt
- 2);
159 phy_modify(phydev
, MII_NWAYTEST
,
160 QCA807X_SMARTSPEED_EN
|
161 QCA807X_SMARTSPEED_RETRY_LIMIT_MASK
,
165 ret
= genphy_soft_reset(phydev
);
170 static int qca807x_get_tunable(struct phy_device
*phydev
,
171 struct ethtool_tunable
*tuna
, void *data
)
174 case ETHTOOL_PHY_DOWNSHIFT
:
175 return qca807x_get_downshift(phydev
, data
);
181 static int qca807x_set_tunable(struct phy_device
*phydev
,
182 struct ethtool_tunable
*tuna
, const void *data
)
185 case ETHTOOL_PHY_DOWNSHIFT
:
186 return qca807x_set_downshift(phydev
, *(const u8
*)data
);
192 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
193 static bool qca807x_distance_valid(int result
)
196 case QCA807X_CDT_RESULTS_OPEN
:
197 case QCA807X_CDT_RESULTS_SAME_SHORT
:
198 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK
:
199 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK
:
200 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK
:
201 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN
:
202 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN
:
203 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN
:
204 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT
:
205 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT
:
206 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT
:
212 static int qca807x_report_length(struct phy_device
*phydev
,
213 int pair
, int result
)
218 ret
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, QCA807X_CDT_MMD3_MDI0_LENGTH
+ pair
);
223 case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT
:
224 length
= (FIELD_GET(QCA807X_CDT_SAME_SHORT_LENGTH_MASK
, ret
) * 800) / 10;
226 case ETHTOOL_A_CABLE_RESULT_CODE_OPEN
:
227 case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT
:
228 length
= (FIELD_GET(QCA807X_CDT_CROSS_SHORT_LENGTH_MASK
, ret
) * 800) / 10;
232 ethnl_cable_test_fault_length(phydev
, pair
, length
);
237 static int qca807x_cable_test_report_trans(int result
)
240 case QCA807X_CDT_RESULTS_OK
:
241 return ETHTOOL_A_CABLE_RESULT_CODE_OK
;
242 case QCA807X_CDT_RESULTS_OPEN
:
243 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN
;
244 case QCA807X_CDT_RESULTS_SAME_SHORT
:
245 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT
;
246 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK
:
247 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK
:
248 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK
:
249 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN
:
250 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN
:
251 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN
:
252 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT
:
253 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT
:
254 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT
:
255 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT
;
257 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC
;
261 static int qca807x_cable_test_report(struct phy_device
*phydev
)
263 int pair0
, pair1
, pair2
, pair3
;
266 ret
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, QCA807X_CDT_MMD3_STATUS
);
270 pair0
= FIELD_GET(QCA807X_CDT_MDI0_STATUS_MASK
, ret
);
271 pair1
= FIELD_GET(QCA807X_CDT_MDI1_STATUS_MASK
, ret
);
272 pair2
= FIELD_GET(QCA807X_CDT_MDI2_STATUS_MASK
, ret
);
273 pair3
= FIELD_GET(QCA807X_CDT_MDI3_STATUS_MASK
, ret
);
275 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_A
,
276 qca807x_cable_test_report_trans(pair0
));
277 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_B
,
278 qca807x_cable_test_report_trans(pair1
));
279 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_C
,
280 qca807x_cable_test_report_trans(pair2
));
281 ethnl_cable_test_result(phydev
, ETHTOOL_A_CABLE_PAIR_D
,
282 qca807x_cable_test_report_trans(pair3
));
284 if (qca807x_distance_valid(pair0
))
285 qca807x_report_length(phydev
, 0, qca807x_cable_test_report_trans(pair0
));
286 if (qca807x_distance_valid(pair1
))
287 qca807x_report_length(phydev
, 1, qca807x_cable_test_report_trans(pair1
));
288 if (qca807x_distance_valid(pair2
))
289 qca807x_report_length(phydev
, 2, qca807x_cable_test_report_trans(pair2
));
290 if (qca807x_distance_valid(pair3
))
291 qca807x_report_length(phydev
, 3, qca807x_cable_test_report_trans(pair3
));
296 static int qca807x_cable_test_get_status(struct phy_device
*phydev
,
303 val
= phy_read(phydev
, QCA807X_CDT
);
304 if (!((val
& QCA807X_CDT_ENABLE
) && (val
& QCA807X_CDT_STATUS
))) {
307 return qca807x_cable_test_report(phydev
);
313 static int qca807x_cable_test_start(struct phy_device
*phydev
)
317 val
= phy_read(phydev
, QCA807X_CDT
);
318 /* Enable inter-pair short check as well */
319 val
&= ~QCA807X_CDT_ENABLE_INTER_PAIR_SHORT
;
320 val
|= QCA807X_CDT_ENABLE
;
321 ret
= phy_write(phydev
, QCA807X_CDT
, val
);
327 #ifdef CONFIG_GPIOLIB
328 static int qca807x_gpio_get_direction(struct gpio_chip
*gc
, unsigned int offset
)
330 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
331 return GPIO_LINE_DIRECTION_OUT
;
333 return GPIOF_DIR_OUT
;
337 static int qca807x_gpio_get_reg(unsigned int offset
)
339 return QCA807X_MMD7_LED_100N_2
+ (offset
% 2) * 2;
342 static int qca807x_gpio_get(struct gpio_chip
*gc
, unsigned int offset
)
344 struct qca807x_gpio_priv
*priv
= gpiochip_get_data(gc
);
347 val
= phy_read_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
));
349 return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK
, val
);
352 static void qca807x_gpio_set(struct gpio_chip
*gc
, unsigned int offset
, int value
)
354 struct qca807x_gpio_priv
*priv
= gpiochip_get_data(gc
);
357 val
= phy_read_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
));
358 val
&= ~QCA807X_GPIO_FORCE_MODE_MASK
;
359 val
|= QCA807X_GPIO_FORCE_EN
;
360 val
|= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK
, value
);
362 phy_write_mmd(priv
->phy
, MDIO_MMD_AN
, qca807x_gpio_get_reg(offset
), val
);
365 static int qca807x_gpio_dir_out(struct gpio_chip
*gc
, unsigned int offset
, int value
)
367 qca807x_gpio_set(gc
, offset
, value
);
372 static int qca807x_gpio(struct phy_device
*phydev
)
374 struct device
*dev
= &phydev
->mdio
.dev
;
375 struct qca807x_gpio_priv
*priv
;
376 struct gpio_chip
*gc
;
378 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
384 gc
= devm_kzalloc(dev
, sizeof(*gc
), GFP_KERNEL
);
388 gc
->label
= dev_name(dev
);
392 gc
->owner
= THIS_MODULE
;
393 gc
->can_sleep
= true;
394 gc
->get_direction
= qca807x_gpio_get_direction
;
395 gc
->direction_output
= qca807x_gpio_dir_out
;
396 gc
->get
= qca807x_gpio_get
;
397 gc
->set
= qca807x_gpio_set
;
399 return devm_gpiochip_add_data(dev
, gc
, priv
);
403 static int qca807x_read_copper_status(struct phy_device
*phydev
, bool combo_port
)
405 int ss
, err
, page
, old_link
= phydev
->link
;
407 /* Only combo port has dual pages */
409 /* Check whether copper page is set and set if needed */
410 page
= phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
);
411 if (!(page
& QCA807X_BT_BX_REG_SEL
)) {
412 page
|= QCA807X_BT_BX_REG_SEL
;
413 phy_write(phydev
, QCA807X_CHIP_CONFIGURATION
, page
);
417 /* Update the link, but return if there was an error */
418 err
= genphy_update_link(phydev
);
422 /* why bother the PHY if nothing can have changed */
423 if (phydev
->autoneg
== AUTONEG_ENABLE
&& old_link
&& phydev
->link
)
426 phydev
->speed
= SPEED_UNKNOWN
;
427 phydev
->duplex
= DUPLEX_UNKNOWN
;
429 phydev
->asym_pause
= 0;
431 err
= genphy_read_lpa(phydev
);
435 /* Read the QCA807x PHY-Specific Status register copper page,
436 * which indicates the speed and duplex that the PHY is actually
437 * using, irrespective of whether we are in autoneg mode or not.
439 ss
= phy_read(phydev
, QCA807X_PHY_SPECIFIC_STATUS
);
443 if (ss
& QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED
) {
446 sfc
= phy_read(phydev
, QCA807X_FUNCTION_CONTROL
);
450 switch (FIELD_GET(QCA807X_SS_SPEED_MASK
, ss
)) {
451 case QCA807X_SS_SPEED_10
:
452 phydev
->speed
= SPEED_10
;
454 case QCA807X_SS_SPEED_100
:
455 phydev
->speed
= SPEED_100
;
457 case QCA807X_SS_SPEED_1000
:
458 phydev
->speed
= SPEED_1000
;
461 if (ss
& QCA807X_SS_DUPLEX
)
462 phydev
->duplex
= DUPLEX_FULL
;
464 phydev
->duplex
= DUPLEX_HALF
;
466 if (ss
& QCA807X_SS_MDIX
)
467 phydev
->mdix
= ETH_TP_MDI_X
;
469 phydev
->mdix
= ETH_TP_MDI
;
471 switch (FIELD_GET(QCA807X_FC_MDI_CROSSOVER_MODE_MASK
, sfc
)) {
472 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI
:
473 phydev
->mdix_ctrl
= ETH_TP_MDI
;
475 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX
:
476 phydev
->mdix_ctrl
= ETH_TP_MDI_X
;
478 case QCA807X_FC_MDI_CROSSOVER_AUTO
:
479 phydev
->mdix_ctrl
= ETH_TP_MDI_AUTO
;
484 if (phydev
->autoneg
== AUTONEG_ENABLE
&& phydev
->autoneg_complete
)
485 phy_resolve_aneg_pause(phydev
);
490 static int qca807x_read_fiber_status(struct phy_device
*phydev
, bool combo_port
)
492 int ss
, err
, page
, lpa
, old_link
= phydev
->link
;
494 /* Check whether fiber page is set and set if needed */
495 page
= phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
);
496 if (page
& QCA807X_BT_BX_REG_SEL
) {
497 page
&= ~QCA807X_BT_BX_REG_SEL
;
498 phy_write(phydev
, QCA807X_CHIP_CONFIGURATION
, page
);
501 /* Update the link, but return if there was an error */
502 err
= genphy_update_link(phydev
);
506 /* why bother the PHY if nothing can have changed */
507 if (phydev
->autoneg
== AUTONEG_ENABLE
&& old_link
&& phydev
->link
)
510 phydev
->speed
= SPEED_UNKNOWN
;
511 phydev
->duplex
= DUPLEX_UNKNOWN
;
513 phydev
->asym_pause
= 0;
515 if (phydev
->autoneg
== AUTONEG_ENABLE
&& phydev
->autoneg_complete
) {
516 lpa
= phy_read(phydev
, MII_LPA
);
520 linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
,
521 phydev
->lp_advertising
, lpa
& LPA_LPACK
);
522 linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT
,
523 phydev
->lp_advertising
, lpa
& LPA_1000XFULL
);
524 linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT
,
525 phydev
->lp_advertising
, lpa
& LPA_1000XPAUSE
);
526 linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT
,
527 phydev
->lp_advertising
,
528 lpa
& LPA_1000XPAUSE_ASYM
);
530 phy_resolve_aneg_linkmode(phydev
);
533 /* Read the QCA807x PHY-Specific Status register fiber page,
534 * which indicates the speed and duplex that the PHY is actually
535 * using, irrespective of whether we are in autoneg mode or not.
537 ss
= phy_read(phydev
, QCA807X_PHY_SPECIFIC_STATUS
);
541 if (ss
& QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED
) {
542 switch (FIELD_GET(QCA807X_SS_SPEED_MASK
, ss
)) {
543 case QCA807X_SS_SPEED_100
:
544 phydev
->speed
= SPEED_100
;
546 case QCA807X_SS_SPEED_1000
:
547 phydev
->speed
= SPEED_1000
;
551 if (ss
& QCA807X_SS_DUPLEX
)
552 phydev
->duplex
= DUPLEX_FULL
;
554 phydev
->duplex
= DUPLEX_HALF
;
560 static int qca807x_read_status(struct phy_device
*phydev
)
564 /* Check for Combo port */
565 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
566 /* Check for fiber mode first */
567 if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->supported
)) {
568 /* Check for actual detected media */
569 val
= phy_read(phydev
, QCA807X_MEDIA_SELECT_STATUS
);
570 if (val
& QCA807X_MEDIA_DETECTED_COPPER
) {
571 qca807x_read_copper_status(phydev
, true);
572 } else if ((val
& QCA807X_MEDIA_DETECTED_1000_BASE_X
) ||
573 (val
& QCA807X_MEDIA_DETECTED_100_BASE_FX
)) {
574 qca807x_read_fiber_status(phydev
, true);
577 qca807x_read_copper_status(phydev
, true);
580 qca807x_read_copper_status(phydev
, false);
586 static int qca807x_config_intr(struct phy_device
*phydev
)
590 val
= phy_read(phydev
, QCA807X_INTR_ENABLE
);
592 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
593 /* Check for combo port as it has fewer interrupts */
594 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
595 val
|= QCA807X_INTR_ENABLE_SPEED_CHANGED
;
596 val
|= QCA807X_INTR_ENABLE_LINK_FAIL
;
597 val
|= QCA807X_INTR_ENABLE_LINK_SUCCESS
;
599 val
|= QCA807X_INTR_ENABLE_AUTONEG_ERR
;
600 val
|= QCA807X_INTR_ENABLE_SPEED_CHANGED
;
601 val
|= QCA807X_INTR_ENABLE_DUPLEX_CHANGED
;
602 val
|= QCA807X_INTR_ENABLE_LINK_FAIL
;
603 val
|= QCA807X_INTR_ENABLE_LINK_SUCCESS
;
605 ret
= phy_write(phydev
, QCA807X_INTR_ENABLE
, val
);
607 ret
= phy_write(phydev
, QCA807X_INTR_ENABLE
, 0);
613 static int qca807x_ack_intr(struct phy_device
*phydev
)
617 ret
= phy_read(phydev
, QCA807X_INTR_STATUS
);
619 return (ret
< 0) ? ret
: 0;
622 static int qca807x_led_config(struct phy_device
*phydev
)
624 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
625 bool led_config
= false;
628 val
= phy_read_mmd(phydev
, MDIO_MMD_AN
, QCA807X_MMD7_LED_1000N_1
);
632 if (of_property_read_bool(node
, "qcom,single-led-1000")) {
633 val
|= QCA807X_LED_TXACT_BLK_EN_2
;
634 val
|= QCA807X_LED_RXACT_BLK_EN_2
;
635 val
|= QCA807X_LED_GT_ON_EN_2
;
640 if (of_property_read_bool(node
, "qcom,single-led-100")) {
641 val
|= QCA807X_LED_HT_ON_EN_2
;
646 if (of_property_read_bool(node
, "qcom,single-led-10")) {
647 val
|= QCA807X_LED_BT_ON_EN_2
;
653 return phy_write_mmd(phydev
, MDIO_MMD_AN
, QCA807X_MMD7_LED_1000N_1
, val
);
658 static const struct sfp_upstream_ops qca807x_sfp_ops
= {
659 .attach
= phy_sfp_attach
,
660 .detach
= phy_sfp_detach
,
663 static int qca807x_config(struct phy_device
*phydev
)
665 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
666 int control_dac
, ret
= 0;
669 /* Check for Combo port */
670 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
)) {
671 int fiber_mode_autodect
;
675 if (of_property_read_bool(node
, "qcom,fiber-enable")) {
676 /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
677 fiber_mode_autodect
= phy_read_mmd(phydev
, MDIO_MMD_AN
,
678 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION
);
679 fiber_mode_autodect
|= QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN
;
680 phy_write_mmd(phydev
, MDIO_MMD_AN
, QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION
,
681 fiber_mode_autodect
);
683 /* Enable 4 copper + combo port mode */
684 chip_config
= phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
);
685 chip_config
&= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK
;
686 chip_config
|= FIELD_PREP(QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK
,
687 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER
);
688 phy_write(phydev
, QCA807X_CHIP_CONFIGURATION
, chip_config
);
690 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->supported
);
691 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, phydev
->advertising
);
694 /* Prevent PSGMII going into hibernation via PSGMII self test */
695 psgmii_serdes
= phy_read_mmd(phydev
, MDIO_MMD_PCS
, PSGMII_MMD3_SERDES_CONTROL
);
696 psgmii_serdes
&= ~BIT(1);
697 ret
= phy_write_mmd(phydev
, MDIO_MMD_PCS
,
698 PSGMII_MMD3_SERDES_CONTROL
,
702 if (!of_property_read_u32(node
, "qcom,control-dac", &of_control_dac
)) {
703 control_dac
= phy_read_mmd(phydev
, MDIO_MMD_AN
,
704 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH
);
705 control_dac
&= ~QCA807X_CONTROL_DAC_MASK
;
706 control_dac
|= FIELD_PREP(QCA807X_CONTROL_DAC_MASK
, of_control_dac
);
707 ret
= phy_write_mmd(phydev
, MDIO_MMD_AN
,
708 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH
,
712 /* Optionally configure LED-s */
713 if (IS_ENABLED(CONFIG_GPIOLIB
)) {
714 /* Check whether PHY-s pins are used as GPIO-s */
715 if (!of_property_read_bool(node
, "gpio-controller"))
716 ret
= qca807x_led_config(phydev
);
718 ret
= qca807x_led_config(phydev
);
724 static int qca807x_probe(struct phy_device
*phydev
)
726 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
729 if (IS_ENABLED(CONFIG_GPIOLIB
)) {
730 /* Do not register a GPIO controller unless flagged for it */
731 if (of_property_read_bool(node
, "gpio-controller"))
732 ret
= qca807x_gpio(phydev
);
735 /* Attach SFP bus on combo port*/
736 if (of_property_read_bool(node
, "qcom,fiber-enable")) {
737 if (phy_read(phydev
, QCA807X_CHIP_CONFIGURATION
))
738 ret
= phy_sfp_probe(phydev
, &qca807x_sfp_ops
);
744 static int qca807x_psgmii_config(struct phy_device
*phydev
)
746 struct device_node
*node
= phydev
->mdio
.dev
.of_node
;
747 int psgmii_az
, tx_amp
, ret
= 0;
748 u32 tx_driver_strength
;
750 /* Workaround to enable AZ transmitting ability */
751 if (of_property_read_bool(node
, "qcom,psgmii-az")) {
752 psgmii_az
= phy_read_mmd(phydev
, MDIO_MMD_PMAPMD
, PSGMII_MODE_CTRL
);
753 psgmii_az
&= ~PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK
;
754 psgmii_az
|= FIELD_PREP(PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK
, 0xc);
755 ret
= phy_write_mmd(phydev
, MDIO_MMD_PMAPMD
, PSGMII_MODE_CTRL
, psgmii_az
);
756 psgmii_az
= phy_read_mmd(phydev
, MDIO_MMD_PMAPMD
, PSGMII_MODE_CTRL
);
759 /* PSGMII/QSGMII TX amp set to DT defined value instead of default 600mV */
760 if (!of_property_read_u32(node
, "qcom,tx-driver-strength", &tx_driver_strength
)) {
761 tx_amp
= phy_read(phydev
, PSGMII_QSGMII_DRIVE_CONTROL_1
);
762 tx_amp
&= ~PSGMII_QSGMII_TX_DRIVER_MASK
;
763 tx_amp
|= FIELD_PREP(PSGMII_QSGMII_TX_DRIVER_MASK
, tx_driver_strength
);
764 ret
= phy_write(phydev
, PSGMII_QSGMII_DRIVE_CONTROL_1
, tx_amp
);
770 static struct phy_driver qca807x_drivers
[] = {
772 PHY_ID_MATCH_EXACT(PHY_ID_QCA8072
),
773 .name
= "Qualcomm QCA8072",
774 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
775 .flags
= PHY_POLL_CABLE_TEST
,
777 /* PHY_GBIT_FEATURES */
778 .probe
= qca807x_probe
,
779 .config_init
= qca807x_config
,
780 .read_status
= qca807x_read_status
,
781 .config_intr
= qca807x_config_intr
,
782 .ack_interrupt
= qca807x_ack_intr
,
783 .soft_reset
= genphy_soft_reset
,
784 .get_tunable
= qca807x_get_tunable
,
785 .set_tunable
= qca807x_set_tunable
,
786 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
787 .cable_test_start
= qca807x_cable_test_start
,
788 .cable_test_get_status
= qca807x_cable_test_get_status
,
792 PHY_ID_MATCH_EXACT(PHY_ID_QCA8075
),
793 .name
= "Qualcomm QCA8075",
794 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
795 .flags
= PHY_POLL_CABLE_TEST
,
797 /* PHY_GBIT_FEATURES */
798 .probe
= qca807x_probe
,
799 .config_init
= qca807x_config
,
800 .read_status
= qca807x_read_status
,
801 .config_intr
= qca807x_config_intr
,
802 .ack_interrupt
= qca807x_ack_intr
,
803 .soft_reset
= genphy_soft_reset
,
804 .get_tunable
= qca807x_get_tunable
,
805 .set_tunable
= qca807x_set_tunable
,
806 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
807 .cable_test_start
= qca807x_cable_test_start
,
808 .cable_test_get_status
= qca807x_cable_test_get_status
,
812 PHY_ID_MATCH_EXACT(PHY_ID_QCA807X_PSGMII
),
813 .name
= "Qualcomm QCA807x PSGMII",
814 .probe
= qca807x_psgmii_config
,
817 module_phy_driver(qca807x_drivers
);
819 static struct mdio_device_id __maybe_unused qca807x_tbl
[] = {
820 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072
) },
821 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075
) },
822 { PHY_ID_MATCH_MODEL(PHY_ID_QCA807X_PSGMII
) },
826 MODULE_AUTHOR("Robert Marko");
827 MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
828 MODULE_DEVICE_TABLE(mdio
, qca807x_tbl
);
829 MODULE_LICENSE("GPL");