|
32 | 32 | #define RTL8211F_PHYCR2 0x19 |
33 | 33 | #define RTL8211F_INSR 0x1d |
34 | 34 |
|
| 35 | +#define RTL8211F_LEDCR 0x10 |
| 36 | +#define RTL8211F_LEDCR_MODE BIT(15) |
| 37 | +#define RTL8211F_LEDCR_ACT_TXRX BIT(4) |
| 38 | +#define RTL8211F_LEDCR_LINK_1000 BIT(3) |
| 39 | +#define RTL8211F_LEDCR_LINK_100 BIT(1) |
| 40 | +#define RTL8211F_LEDCR_LINK_10 BIT(0) |
| 41 | +#define RTL8211F_LEDCR_MASK GENMASK(4, 0) |
| 42 | +#define RTL8211F_LEDCR_SHIFT 5 |
| 43 | + |
35 | 44 | #define RTL8211F_TX_DELAY BIT(8) |
36 | 45 | #define RTL8211F_RX_DELAY BIT(3) |
37 | 46 |
|
|
73 | 82 | #define RTL_GENERIC_PHYID 0x001cc800 |
74 | 83 | #define RTL_8211FVD_PHYID 0x001cc878 |
75 | 84 |
|
| 85 | +#define RTL8211F_LED_COUNT 3 |
| 86 | + |
76 | 87 | MODULE_DESCRIPTION("Realtek PHY driver"); |
77 | 88 | MODULE_AUTHOR("Johnson Leung"); |
78 | 89 | MODULE_LICENSE("GPL"); |
@@ -462,6 +473,98 @@ static int rtl821x_resume(struct phy_device *phydev) |
462 | 473 | return 0; |
463 | 474 | } |
464 | 475 |
|
| 476 | +static int rtl8211f_led_hw_is_supported(struct phy_device *phydev, u8 index, |
| 477 | + unsigned long rules) |
| 478 | +{ |
| 479 | + const unsigned long mask = BIT(TRIGGER_NETDEV_LINK_10) | |
| 480 | + BIT(TRIGGER_NETDEV_LINK_100) | |
| 481 | + BIT(TRIGGER_NETDEV_LINK_1000) | |
| 482 | + BIT(TRIGGER_NETDEV_RX) | |
| 483 | + BIT(TRIGGER_NETDEV_TX); |
| 484 | + |
| 485 | + /* The RTL8211F PHY supports these LED settings on up to three LEDs: |
| 486 | + * - Link: Configurable subset of 10/100/1000 link rates |
| 487 | + * - Active: Blink on activity, RX or TX is not differentiated |
| 488 | + * The Active option has two modes, A and B: |
| 489 | + * - A: Link and Active indication at configurable, but matching, |
| 490 | + * subset of 10/100/1000 link rates |
| 491 | + * - B: Link indication at configurable subset of 10/100/1000 link |
| 492 | + * rates and Active indication always at all three 10+100+1000 |
| 493 | + * link rates. |
| 494 | + * This code currently uses mode B only. |
| 495 | + */ |
| 496 | + |
| 497 | + if (index >= RTL8211F_LED_COUNT) |
| 498 | + return -EINVAL; |
| 499 | + |
| 500 | + /* Filter out any other unsupported triggers. */ |
| 501 | + if (rules & ~mask) |
| 502 | + return -EOPNOTSUPP; |
| 503 | + |
| 504 | + /* RX and TX are not differentiated, either both are set or not set. */ |
| 505 | + if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX))) |
| 506 | + return -EOPNOTSUPP; |
| 507 | + |
| 508 | + return 0; |
| 509 | +} |
| 510 | + |
| 511 | +static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index, |
| 512 | + unsigned long *rules) |
| 513 | +{ |
| 514 | + int val; |
| 515 | + |
| 516 | + val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR); |
| 517 | + if (val < 0) |
| 518 | + return val; |
| 519 | + |
| 520 | + val >>= RTL8211F_LEDCR_SHIFT * index; |
| 521 | + val &= RTL8211F_LEDCR_MASK; |
| 522 | + |
| 523 | + if (val & RTL8211F_LEDCR_LINK_10) |
| 524 | + set_bit(TRIGGER_NETDEV_LINK_10, rules); |
| 525 | + |
| 526 | + if (val & RTL8211F_LEDCR_LINK_100) |
| 527 | + set_bit(TRIGGER_NETDEV_LINK_100, rules); |
| 528 | + |
| 529 | + if (val & RTL8211F_LEDCR_LINK_1000) |
| 530 | + set_bit(TRIGGER_NETDEV_LINK_1000, rules); |
| 531 | + |
| 532 | + if (val & RTL8211F_LEDCR_ACT_TXRX) { |
| 533 | + set_bit(TRIGGER_NETDEV_RX, rules); |
| 534 | + set_bit(TRIGGER_NETDEV_TX, rules); |
| 535 | + } |
| 536 | + |
| 537 | + return 0; |
| 538 | +} |
| 539 | + |
| 540 | +static int rtl8211f_led_hw_control_set(struct phy_device *phydev, u8 index, |
| 541 | + unsigned long rules) |
| 542 | +{ |
| 543 | + const u16 mask = RTL8211F_LEDCR_MASK << (RTL8211F_LEDCR_SHIFT * index); |
| 544 | + u16 reg = RTL8211F_LEDCR_MODE; /* Mode B */ |
| 545 | + |
| 546 | + if (index >= RTL8211F_LED_COUNT) |
| 547 | + return -EINVAL; |
| 548 | + |
| 549 | + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) |
| 550 | + reg |= RTL8211F_LEDCR_LINK_10; |
| 551 | + |
| 552 | + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) |
| 553 | + reg |= RTL8211F_LEDCR_LINK_100; |
| 554 | + |
| 555 | + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) |
| 556 | + reg |= RTL8211F_LEDCR_LINK_1000; |
| 557 | + |
| 558 | + if (test_bit(TRIGGER_NETDEV_RX, &rules) || |
| 559 | + test_bit(TRIGGER_NETDEV_TX, &rules)) { |
| 560 | + reg |= RTL8211F_LEDCR_ACT_TXRX; |
| 561 | + } |
| 562 | + |
| 563 | + reg <<= RTL8211F_LEDCR_SHIFT * index; |
| 564 | + |
| 565 | + return phy_modify_paged(phydev, 0xd04, RTL8211F_LEDCR, mask, reg); |
| 566 | +} |
| 567 | + |
465 | 568 | static int rtl8211e_config_init(struct phy_device *phydev) |
466 | 569 | { |
467 | 570 | int ret = 0, oldpage; |
@@ -962,6 +1065,9 @@ static struct phy_driver realtek_drvs[] = { |
962 | 1065 | .read_page = rtl821x_read_page, |
963 | 1066 | .write_page = rtl821x_write_page, |
964 | 1067 | .flags = PHY_ALWAYS_CALL_SUSPEND, |
| 1068 | + .led_hw_is_supported = rtl8211f_led_hw_is_supported, |
| 1069 | + .led_hw_control_get = rtl8211f_led_hw_control_get, |
| 1070 | + .led_hw_control_set = rtl8211f_led_hw_control_set, |
965 | 1071 | }, { |
966 | 1072 | PHY_ID_MATCH_EXACT(RTL_8211FVD_PHYID), |
967 | 1073 | .name = "RTL8211F-VD Gigabit Ethernet", |
|
0 commit comments