abfc11e121c1bf7ac5099c1427f46d12f2b2ff8a
[openwrt/staging/dangole.git] / target / linux / ipq40xx / files / drivers / net / phy / qca807x.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 Sartura Ltd.
4 *
5 * Author: Robert Marko <robert.marko@sartura.hr>
6 *
7 * Qualcomm QCA8072 and QCA8075 PHY driver
8 */
9
10 #include <linux/version.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/phy.h>
14 #include <linux/bitfield.h>
15 #include <linux/ethtool_netlink.h>
16 #include <linux/gpio.h>
17 #include <linux/sfp.h>
18
19 #include <dt-bindings/net/qcom-qca807x.h>
20
21 #define PHY_ID_QCA8072 0x004dd0b2
22 #define PHY_ID_QCA8075 0x004dd0b1
23 #define PHY_ID_QCA807X_PSGMII 0x06820805
24
25 /* Downshift */
26 #define QCA807X_SMARTSPEED_EN BIT(5)
27 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MASK GENMASK(4, 2)
28 #define QCA807X_SMARTSPEED_RETRY_LIMIT_DEFAULT 5
29 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MIN 2
30 #define QCA807X_SMARTSPEED_RETRY_LIMIT_MAX 9
31
32 /* Cable diagnostic test (CDT) */
33 #define QCA807X_CDT 0x16
34 #define QCA807X_CDT_ENABLE BIT(15)
35 #define QCA807X_CDT_ENABLE_INTER_PAIR_SHORT BIT(13)
36 #define QCA807X_CDT_STATUS BIT(11)
37 #define QCA807X_CDT_MMD3_STATUS 0x8064
38 #define QCA807X_CDT_MDI0_STATUS_MASK GENMASK(15, 12)
39 #define QCA807X_CDT_MDI1_STATUS_MASK GENMASK(11, 8)
40 #define QCA807X_CDT_MDI2_STATUS_MASK GENMASK(7, 4)
41 #define QCA807X_CDT_MDI3_STATUS_MASK GENMASK(3, 0)
42 #define QCA807X_CDT_RESULTS_INVALID 0x0
43 #define QCA807X_CDT_RESULTS_OK 0x1
44 #define QCA807X_CDT_RESULTS_OPEN 0x2
45 #define QCA807X_CDT_RESULTS_SAME_SHORT 0x3
46 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK 0x4
47 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK 0x8
48 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK 0xc
49 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN 0x6
50 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN 0xa
51 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN 0xe
52 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT 0x7
53 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT 0xb
54 #define QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT 0xf
55 #define QCA807X_CDT_RESULTS_BUSY 0x9
56 #define QCA807X_CDT_MMD3_MDI0_LENGTH 0x8065
57 #define QCA807X_CDT_MMD3_MDI1_LENGTH 0x8066
58 #define QCA807X_CDT_MMD3_MDI2_LENGTH 0x8067
59 #define QCA807X_CDT_MMD3_MDI3_LENGTH 0x8068
60 #define QCA807X_CDT_SAME_SHORT_LENGTH_MASK GENMASK(15, 8)
61 #define QCA807X_CDT_CROSS_SHORT_LENGTH_MASK GENMASK(7, 0)
62
63 #define QCA807X_CHIP_CONFIGURATION 0x1f
64 #define QCA807X_BT_BX_REG_SEL BIT(15)
65 #define QCA807X_BT_BX_REG_SEL_FIBER 0
66 #define QCA807X_BT_BX_REG_SEL_COPPER 1
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
71
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)
76
77 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
78 #define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
79
80 #define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
81 #define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
82
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)
94
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)
102
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
108
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)
117
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 BIT(0)
123 #define PSGMII_MMD3_SERDES_CONTROL 0x805a
124
125 struct qca807x_gpio_priv {
126 struct phy_device *phy;
127 };
128
129 static int qca807x_get_downshift(struct phy_device *phydev, u8 *data)
130 {
131 int val, cnt, enable;
132
133 val = phy_read(phydev, MII_NWAYTEST);
134 if (val < 0)
135 return val;
136
137 enable = FIELD_GET(QCA807X_SMARTSPEED_EN, val);
138 cnt = FIELD_GET(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK, val) + 2;
139
140 *data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
141
142 return 0;
143 }
144
145 static int qca807x_set_downshift(struct phy_device *phydev, u8 cnt)
146 {
147 int ret, val;
148
149 if (cnt > QCA807X_SMARTSPEED_RETRY_LIMIT_MAX ||
150 (cnt < QCA807X_SMARTSPEED_RETRY_LIMIT_MIN && cnt != DOWNSHIFT_DEV_DISABLE))
151 return -EINVAL;
152
153 if (!cnt) {
154 ret = phy_clear_bits(phydev, MII_NWAYTEST, QCA807X_SMARTSPEED_EN);
155 } else {
156 val = QCA807X_SMARTSPEED_EN;
157 val |= FIELD_PREP(QCA807X_SMARTSPEED_RETRY_LIMIT_MASK, cnt - 2);
158
159 phy_modify(phydev, MII_NWAYTEST,
160 QCA807X_SMARTSPEED_EN |
161 QCA807X_SMARTSPEED_RETRY_LIMIT_MASK,
162 val);
163 }
164
165 ret = genphy_soft_reset(phydev);
166
167 return ret;
168 }
169
170 static int qca807x_get_tunable(struct phy_device *phydev,
171 struct ethtool_tunable *tuna, void *data)
172 {
173 switch (tuna->id) {
174 case ETHTOOL_PHY_DOWNSHIFT:
175 return qca807x_get_downshift(phydev, data);
176 default:
177 return -EOPNOTSUPP;
178 }
179 }
180
181 static int qca807x_set_tunable(struct phy_device *phydev,
182 struct ethtool_tunable *tuna, const void *data)
183 {
184 switch (tuna->id) {
185 case ETHTOOL_PHY_DOWNSHIFT:
186 return qca807x_set_downshift(phydev, *(const u8 *)data);
187 default:
188 return -EOPNOTSUPP;
189 }
190 }
191
192 static bool qca807x_distance_valid(int result)
193 {
194 switch (result) {
195 case QCA807X_CDT_RESULTS_OPEN:
196 case QCA807X_CDT_RESULTS_SAME_SHORT:
197 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK:
198 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK:
199 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK:
200 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
201 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
202 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
203 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
204 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
205 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
206 return true;
207 }
208 return false;
209 }
210
211 static int qca807x_report_length(struct phy_device *phydev,
212 int pair, int result)
213 {
214 int length;
215 int ret;
216
217 ret = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA807X_CDT_MMD3_MDI0_LENGTH + pair);
218 if (ret < 0)
219 return ret;
220
221 switch (result) {
222 case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
223 length = (FIELD_GET(QCA807X_CDT_SAME_SHORT_LENGTH_MASK, ret) * 800) / 10;
224 break;
225 case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
226 case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
227 length = (FIELD_GET(QCA807X_CDT_CROSS_SHORT_LENGTH_MASK, ret) * 800) / 10;
228 break;
229 }
230
231 ethnl_cable_test_fault_length(phydev, pair, length);
232
233 return 0;
234 }
235
236 static int qca807x_cable_test_report_trans(int result)
237 {
238 switch (result) {
239 case QCA807X_CDT_RESULTS_OK:
240 return ETHTOOL_A_CABLE_RESULT_CODE_OK;
241 case QCA807X_CDT_RESULTS_OPEN:
242 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
243 case QCA807X_CDT_RESULTS_SAME_SHORT:
244 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
245 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OK:
246 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OK:
247 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OK:
248 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
249 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
250 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
251 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
252 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
253 case QCA807X_CDT_RESULTS_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
254 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
255 default:
256 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
257 }
258 }
259
260 static int qca807x_cable_test_report(struct phy_device *phydev)
261 {
262 int pair0, pair1, pair2, pair3;
263 int ret;
264
265 ret = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA807X_CDT_MMD3_STATUS);
266 if (ret < 0)
267 return ret;
268
269 pair0 = FIELD_GET(QCA807X_CDT_MDI0_STATUS_MASK, ret);
270 pair1 = FIELD_GET(QCA807X_CDT_MDI1_STATUS_MASK, ret);
271 pair2 = FIELD_GET(QCA807X_CDT_MDI2_STATUS_MASK, ret);
272 pair3 = FIELD_GET(QCA807X_CDT_MDI3_STATUS_MASK, ret);
273
274 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
275 qca807x_cable_test_report_trans(pair0));
276 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
277 qca807x_cable_test_report_trans(pair1));
278 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
279 qca807x_cable_test_report_trans(pair2));
280 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
281 qca807x_cable_test_report_trans(pair3));
282
283 if (qca807x_distance_valid(pair0))
284 qca807x_report_length(phydev, 0, qca807x_cable_test_report_trans(pair0));
285 if (qca807x_distance_valid(pair1))
286 qca807x_report_length(phydev, 1, qca807x_cable_test_report_trans(pair1));
287 if (qca807x_distance_valid(pair2))
288 qca807x_report_length(phydev, 2, qca807x_cable_test_report_trans(pair2));
289 if (qca807x_distance_valid(pair3))
290 qca807x_report_length(phydev, 3, qca807x_cable_test_report_trans(pair3));
291
292 return 0;
293 }
294
295 static int qca807x_cable_test_get_status(struct phy_device *phydev,
296 bool *finished)
297 {
298 int val;
299
300 *finished = false;
301
302 val = phy_read(phydev, QCA807X_CDT);
303 if (!((val & QCA807X_CDT_ENABLE) && (val & QCA807X_CDT_STATUS))) {
304 *finished = true;
305
306 return qca807x_cable_test_report(phydev);
307 }
308
309 return 0;
310 }
311
312 static int qca807x_cable_test_start(struct phy_device *phydev)
313 {
314 int val, ret;
315
316 val = phy_read(phydev, QCA807X_CDT);
317 /* Enable inter-pair short check as well */
318 val &= ~QCA807X_CDT_ENABLE_INTER_PAIR_SHORT;
319 val |= QCA807X_CDT_ENABLE;
320 ret = phy_write(phydev, QCA807X_CDT, val);
321
322 return ret;
323 }
324
325 #ifdef CONFIG_GPIOLIB
326 static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
327 {
328 return GPIO_LINE_DIRECTION_OUT;
329 }
330
331 static int qca807x_gpio_get_reg(unsigned int offset)
332 {
333 return QCA807X_MMD7_LED_100N_2 + (offset % 2) * 2;
334 }
335
336 static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
337 {
338 struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
339 int val;
340
341 val = phy_read_mmd(priv->phy, MDIO_MMD_AN, qca807x_gpio_get_reg(offset));
342
343 return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
344 }
345
346 static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
347 {
348 struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
349 int val;
350
351 val = phy_read_mmd(priv->phy, MDIO_MMD_AN, qca807x_gpio_get_reg(offset));
352 val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
353 val |= QCA807X_GPIO_FORCE_EN;
354 val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
355
356 phy_write_mmd(priv->phy, MDIO_MMD_AN, qca807x_gpio_get_reg(offset), val);
357 }
358
359 static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
360 {
361 qca807x_gpio_set(gc, offset, value);
362
363 return 0;
364 }
365
366 static int qca807x_gpio(struct phy_device *phydev)
367 {
368 struct device *dev = &phydev->mdio.dev;
369 struct qca807x_gpio_priv *priv;
370 struct gpio_chip *gc;
371
372 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
373 if (!priv)
374 return -ENOMEM;
375
376 priv->phy = phydev;
377
378 gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
379 if (!gc)
380 return -ENOMEM;
381
382 gc->label = dev_name(dev);
383 gc->base = -1;
384 gc->ngpio = 2;
385 gc->parent = dev;
386 gc->owner = THIS_MODULE;
387 gc->can_sleep = true;
388 gc->get_direction = qca807x_gpio_get_direction;
389 gc->direction_output = qca807x_gpio_dir_out;
390 gc->get = qca807x_gpio_get;
391 gc->set = qca807x_gpio_set;
392
393 return devm_gpiochip_add_data(dev, gc, priv);
394 }
395 #endif
396
397 static int qca807x_read_copper_status(struct phy_device *phydev)
398 {
399 int ss, err, old_link = phydev->link;
400
401 /* Update the link, but return if there was an error */
402 err = genphy_update_link(phydev);
403 if (err)
404 return err;
405
406 /* why bother the PHY if nothing can have changed */
407 if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
408 return 0;
409
410 phydev->speed = SPEED_UNKNOWN;
411 phydev->duplex = DUPLEX_UNKNOWN;
412 phydev->pause = 0;
413 phydev->asym_pause = 0;
414
415 err = genphy_read_lpa(phydev);
416 if (err < 0)
417 return err;
418
419 /* Read the QCA807x PHY-Specific Status register copper page,
420 * which indicates the speed and duplex that the PHY is actually
421 * using, irrespective of whether we are in autoneg mode or not.
422 */
423 ss = phy_read(phydev, QCA807X_PHY_SPECIFIC_STATUS);
424 if (ss < 0)
425 return ss;
426
427 if (ss & QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED) {
428 int sfc;
429
430 sfc = phy_read(phydev, QCA807X_FUNCTION_CONTROL);
431 if (sfc < 0)
432 return sfc;
433
434 switch (FIELD_GET(QCA807X_SS_SPEED_MASK, ss)) {
435 case QCA807X_SS_SPEED_10:
436 phydev->speed = SPEED_10;
437 break;
438 case QCA807X_SS_SPEED_100:
439 phydev->speed = SPEED_100;
440 break;
441 case QCA807X_SS_SPEED_1000:
442 phydev->speed = SPEED_1000;
443 break;
444 }
445 if (ss & QCA807X_SS_DUPLEX)
446 phydev->duplex = DUPLEX_FULL;
447 else
448 phydev->duplex = DUPLEX_HALF;
449
450 if (ss & QCA807X_SS_MDIX)
451 phydev->mdix = ETH_TP_MDI_X;
452 else
453 phydev->mdix = ETH_TP_MDI;
454
455 switch (FIELD_GET(QCA807X_FC_MDI_CROSSOVER_MODE_MASK, sfc)) {
456 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI:
457 phydev->mdix_ctrl = ETH_TP_MDI;
458 break;
459 case QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX:
460 phydev->mdix_ctrl = ETH_TP_MDI_X;
461 break;
462 case QCA807X_FC_MDI_CROSSOVER_AUTO:
463 phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
464 break;
465 }
466 }
467
468 if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
469 phy_resolve_aneg_pause(phydev);
470
471 return 0;
472 }
473
474 static int qca807x_read_fiber_status(struct phy_device *phydev)
475 {
476 int ss, err, lpa, old_link = phydev->link;
477
478 /* Update the link, but return if there was an error */
479 err = genphy_update_link(phydev);
480 if (err)
481 return err;
482
483 /* why bother the PHY if nothing can have changed */
484 if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
485 return 0;
486
487 phydev->speed = SPEED_UNKNOWN;
488 phydev->duplex = DUPLEX_UNKNOWN;
489 phydev->pause = 0;
490 phydev->asym_pause = 0;
491
492 if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
493 lpa = phy_read(phydev, MII_LPA);
494 if (lpa < 0)
495 return lpa;
496
497 linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
498 phydev->lp_advertising, lpa & LPA_LPACK);
499 linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
500 phydev->lp_advertising, lpa & LPA_1000XFULL);
501 linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
502 phydev->lp_advertising, lpa & LPA_1000XPAUSE);
503 linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
504 phydev->lp_advertising,
505 lpa & LPA_1000XPAUSE_ASYM);
506
507 phy_resolve_aneg_linkmode(phydev);
508 }
509
510 /* Read the QCA807x PHY-Specific Status register fiber page,
511 * which indicates the speed and duplex that the PHY is actually
512 * using, irrespective of whether we are in autoneg mode or not.
513 */
514 ss = phy_read(phydev, QCA807X_PHY_SPECIFIC_STATUS);
515 if (ss < 0)
516 return ss;
517
518 if (ss & QCA807X_SS_SPEED_AND_DUPLEX_RESOLVED) {
519 switch (FIELD_GET(QCA807X_SS_SPEED_MASK, ss)) {
520 case QCA807X_SS_SPEED_100:
521 phydev->speed = SPEED_100;
522 break;
523 case QCA807X_SS_SPEED_1000:
524 phydev->speed = SPEED_1000;
525 break;
526 }
527
528 if (ss & QCA807X_SS_DUPLEX)
529 phydev->duplex = DUPLEX_FULL;
530 else
531 phydev->duplex = DUPLEX_HALF;
532 }
533
534 return 0;
535 }
536
537 static int qca807x_read_status(struct phy_device *phydev)
538 {
539 if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
540 switch (phydev->port) {
541 case PORT_FIBRE:
542 return qca807x_read_fiber_status(phydev);
543 case PORT_TP:
544 return qca807x_read_copper_status(phydev);
545 default:
546 return -EINVAL;
547 }
548 } else
549 return qca807x_read_copper_status(phydev);
550 }
551
552 static int qca807x_config_intr(struct phy_device *phydev)
553 {
554 int ret, val;
555
556 val = phy_read(phydev, QCA807X_INTR_ENABLE);
557
558 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
559 /* Check for combo port as it has fewer interrupts */
560 if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
561 val |= QCA807X_INTR_ENABLE_SPEED_CHANGED;
562 val |= QCA807X_INTR_ENABLE_LINK_FAIL;
563 val |= QCA807X_INTR_ENABLE_LINK_SUCCESS;
564 } else {
565 val |= QCA807X_INTR_ENABLE_AUTONEG_ERR;
566 val |= QCA807X_INTR_ENABLE_SPEED_CHANGED;
567 val |= QCA807X_INTR_ENABLE_DUPLEX_CHANGED;
568 val |= QCA807X_INTR_ENABLE_LINK_FAIL;
569 val |= QCA807X_INTR_ENABLE_LINK_SUCCESS;
570 }
571 ret = phy_write(phydev, QCA807X_INTR_ENABLE, val);
572 } else {
573 ret = phy_write(phydev, QCA807X_INTR_ENABLE, 0);
574 }
575
576 return ret;
577 }
578
579 static irqreturn_t qca807x_handle_interrupt(struct phy_device *phydev)
580 {
581 int irq_status, int_enabled;
582
583 irq_status = phy_read(phydev, QCA807X_INTR_STATUS);
584 if (irq_status < 0) {
585 phy_error(phydev);
586 return IRQ_NONE;
587 }
588
589 /* Read the current enabled interrupts */
590 int_enabled = phy_read(phydev, QCA807X_INTR_ENABLE);
591 if (int_enabled < 0) {
592 phy_error(phydev);
593 return IRQ_NONE;
594 }
595
596 /* See if this was one of our enabled interrupts */
597 if (!(irq_status & int_enabled))
598 return IRQ_NONE;
599
600 phy_trigger_machine(phydev);
601
602 return IRQ_HANDLED;
603 }
604
605 static int qca807x_led_config(struct phy_device *phydev)
606 {
607 struct device_node *node = phydev->mdio.dev.of_node;
608 bool led_config = false;
609 int val;
610
611 val = phy_read_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_LED_1000N_1);
612 if (val < 0)
613 return val;
614
615 if (of_property_read_bool(node, "qcom,single-led-1000")) {
616 val |= QCA807X_LED_TXACT_BLK_EN_2;
617 val |= QCA807X_LED_RXACT_BLK_EN_2;
618 val |= QCA807X_LED_GT_ON_EN_2;
619
620 led_config = true;
621 }
622
623 if (of_property_read_bool(node, "qcom,single-led-100")) {
624 val |= QCA807X_LED_HT_ON_EN_2;
625
626 led_config = true;
627 }
628
629 if (of_property_read_bool(node, "qcom,single-led-10")) {
630 val |= QCA807X_LED_BT_ON_EN_2;
631
632 led_config = true;
633 }
634
635 if (led_config)
636 return phy_write_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_LED_1000N_1, val);
637 else
638 return 0;
639 }
640
641 static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
642 {
643 struct phy_device *phydev = upstream;
644 __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
645 phy_interface_t iface;
646 int ret;
647 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
648 DECLARE_PHY_INTERFACE_MASK(interfaces);
649
650 sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
651 #else
652 sfp_parse_support(phydev->sfp_bus, id, support);
653 #endif
654 iface = sfp_select_interface(phydev->sfp_bus, support);
655
656 dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
657
658 switch (iface) {
659 case PHY_INTERFACE_MODE_1000BASEX:
660 case PHY_INTERFACE_MODE_100BASEX:
661 /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
662 ret = phy_modify(phydev,
663 QCA807X_CHIP_CONFIGURATION,
664 QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
665 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
666 /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
667 ret = phy_set_bits_mmd(phydev,
668 MDIO_MMD_AN,
669 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
670 QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
671 /* Select fiber page */
672 ret = phy_clear_bits(phydev,
673 QCA807X_CHIP_CONFIGURATION,
674 QCA807X_BT_BX_REG_SEL);
675
676 phydev->port = PORT_FIBRE;
677 break;
678 default:
679 dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
680 return -EINVAL;
681 }
682
683 return ret;
684 }
685
686 static void qca807x_sfp_remove(void *upstream)
687 {
688 struct phy_device *phydev = upstream;
689
690 /* Select copper page */
691 phy_set_bits(phydev,
692 QCA807X_CHIP_CONFIGURATION,
693 QCA807X_BT_BX_REG_SEL);
694
695 phydev->port = PORT_TP;
696 }
697
698 static const struct sfp_upstream_ops qca807x_sfp_ops = {
699 .attach = phy_sfp_attach,
700 .detach = phy_sfp_detach,
701 .module_insert = qca807x_sfp_insert,
702 .module_remove = qca807x_sfp_remove,
703 };
704
705 static int qca807x_config(struct phy_device *phydev)
706 {
707 struct device_node *node = phydev->mdio.dev.of_node;
708 int control_dac, ret = 0;
709 u32 of_control_dac;
710
711 /* Check for Combo port */
712 if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
713 int psgmii_serdes;
714
715 /* Prevent PSGMII going into hibernation via PSGMII self test */
716 psgmii_serdes = phy_read_mmd(phydev, MDIO_MMD_PCS, PSGMII_MMD3_SERDES_CONTROL);
717 psgmii_serdes &= ~BIT(1);
718 ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
719 PSGMII_MMD3_SERDES_CONTROL,
720 psgmii_serdes);
721 }
722
723 if (!of_property_read_u32(node, "qcom,control-dac", &of_control_dac)) {
724 control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
725 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
726 control_dac &= ~QCA807X_CONTROL_DAC_MASK;
727 control_dac |= FIELD_PREP(QCA807X_CONTROL_DAC_MASK, of_control_dac);
728 ret = phy_write_mmd(phydev, MDIO_MMD_AN,
729 QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
730 control_dac);
731 }
732
733 /* Optionally configure LED-s */
734 if (IS_ENABLED(CONFIG_GPIOLIB)) {
735 /* Check whether PHY-s pins are used as GPIO-s */
736 if (!of_property_read_bool(node, "gpio-controller"))
737 ret = qca807x_led_config(phydev);
738 } else {
739 ret = qca807x_led_config(phydev);
740 }
741
742 return ret;
743 }
744
745 static int qca807x_probe(struct phy_device *phydev)
746 {
747 struct device_node *node = phydev->mdio.dev.of_node;
748 int ret = 0;
749
750 if (IS_ENABLED(CONFIG_GPIOLIB)) {
751 /* Do not register a GPIO controller unless flagged for it */
752 if (of_property_read_bool(node, "gpio-controller"))
753 ret = qca807x_gpio(phydev);
754 }
755
756 /* Attach SFP bus on combo port*/
757 if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
758 ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
759 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
760 linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
761 }
762
763 return ret;
764 }
765
766 static int qca807x_psgmii_config(struct phy_device *phydev)
767 {
768 struct device_node *node = phydev->mdio.dev.of_node;
769 int tx_amp, ret = 0;
770 u32 tx_driver_strength;
771
772 /* Workaround to enable AZ transmitting ability */
773 ret = phy_clear_bits_mmd(phydev,
774 MDIO_MMD_PMAPMD,
775 PSGMII_MODE_CTRL,
776 PSGMII_MODE_CTRL_AZ_WORKAROUND_MASK);
777
778 /* PSGMII/QSGMII TX amp set to DT defined value instead of default 600mV */
779 if (!of_property_read_u32(node, "qcom,tx-driver-strength", &tx_driver_strength)) {
780 tx_amp = phy_read(phydev, PSGMII_QSGMII_DRIVE_CONTROL_1);
781 tx_amp &= ~PSGMII_QSGMII_TX_DRIVER_MASK;
782 tx_amp |= FIELD_PREP(PSGMII_QSGMII_TX_DRIVER_MASK, tx_driver_strength);
783 ret = phy_write(phydev, PSGMII_QSGMII_DRIVE_CONTROL_1, tx_amp);
784 }
785
786 return ret;
787 }
788
789 static struct phy_driver qca807x_drivers[] = {
790 {
791 PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
792 .name = "Qualcomm QCA8072",
793 .flags = PHY_POLL_CABLE_TEST,
794 /* PHY_GBIT_FEATURES */
795 .probe = qca807x_probe,
796 .config_init = qca807x_config,
797 .read_status = qca807x_read_status,
798 .config_intr = qca807x_config_intr,
799 .handle_interrupt = qca807x_handle_interrupt,
800 .soft_reset = genphy_soft_reset,
801 .get_tunable = qca807x_get_tunable,
802 .set_tunable = qca807x_set_tunable,
803 .resume = genphy_resume,
804 .suspend = genphy_suspend,
805 .cable_test_start = qca807x_cable_test_start,
806 .cable_test_get_status = qca807x_cable_test_get_status,
807 },
808 {
809 PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
810 .name = "Qualcomm QCA8075",
811 .flags = PHY_POLL_CABLE_TEST,
812 /* PHY_GBIT_FEATURES */
813 .probe = qca807x_probe,
814 .config_init = qca807x_config,
815 .read_status = qca807x_read_status,
816 .config_intr = qca807x_config_intr,
817 .handle_interrupt = qca807x_handle_interrupt,
818 .soft_reset = genphy_soft_reset,
819 .get_tunable = qca807x_get_tunable,
820 .set_tunable = qca807x_set_tunable,
821 .resume = genphy_resume,
822 .suspend = genphy_suspend,
823 .cable_test_start = qca807x_cable_test_start,
824 .cable_test_get_status = qca807x_cable_test_get_status,
825 },
826 {
827 PHY_ID_MATCH_EXACT(PHY_ID_QCA807X_PSGMII),
828 .name = "Qualcomm QCA807x PSGMII",
829 .probe = qca807x_psgmii_config,
830 },
831 };
832 module_phy_driver(qca807x_drivers);
833
834 static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
835 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
836 { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
837 { PHY_ID_MATCH_MODEL(PHY_ID_QCA807X_PSGMII) },
838 { }
839 };
840
841 MODULE_AUTHOR("Robert Marko");
842 MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
843 MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
844 MODULE_LICENSE("GPL");