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 <ansuelsmth@gmail.com>"); 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); 677