Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: dp83tg720: switch to adaptive polling and remove random delays

Now that the PHY reset logic includes a role-specific asymmetric delay
to avoid synchronized reset deadlocks, the previously used randomized
polling intervals are no longer necessary.

This patch removes the get_random_u32_below()-based logic and introduces
an adaptive polling strategy:
- Fast polling for a short time after link-down
- Slow polling if the link remains down
- Slower polling when the link is up

This balances CPU usage and responsiveness while avoiding reset
collisions. Additionally, the driver still relies on polling for
all link state changes, as interrupt support is not implemented,
and link-up events are not reliably signaled by the PHY.

The polling parameters are now documented in the updated top-of-file
comment.

Co-developed-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: David Jander <david@protonic.nl>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250612104157.2262058-4-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

David Jander and committed by
Jakub Kicinski
cc8aeb0f 491e991f

+53 -37
+53 -37
drivers/net/phy/dp83tg720.c
··· 52 52 * The functions that implement this logic are: 53 53 * - dp83tg720_soft_reset() 54 54 * - dp83tg720_get_next_update_time() 55 + * 56 + * 2. Polling-Based Link Detection and IRQ Support 57 + * ----------------------------------------------- 58 + * Due to the PHY-specific limitation described in section 1, link-up events 59 + * cannot be reliably detected via interrupts on the DP83TG720. Therefore, 60 + * polling is required to detect transitions from link-down to link-up. 61 + * 62 + * While link-down events *can* be detected via IRQs on this PHY, this driver 63 + * currently does **not** implement interrupt support. As a result, all link 64 + * state changes must be detected using polling. 65 + * 66 + * Polling behavior: 67 + * - When the link is up: slow polling (e.g. 1s). 68 + * - When the link just went down: fast polling for a short time. 69 + * - When the link stays down: fallback to slow polling. 70 + * 71 + * This design balances responsiveness and CPU usage. It sacrifices fast link-up 72 + * times in cases where the link is expected to remain down for extended periods, 73 + * assuming that such systems do not require immediate reactivity. 55 74 */ 56 75 57 76 /* 58 77 * DP83TG720S_POLL_ACTIVE_LINK - Polling interval in milliseconds when the link 59 78 * is active. 60 - * DP83TG720S_POLL_NO_LINK_MIN - Minimum polling interval in milliseconds when 61 - * the link is down. 62 - * DP83TG720S_POLL_NO_LINK_MAX - Maximum polling interval in milliseconds when 63 - * the link is down. 79 + * DP83TG720S_POLL_NO_LINK - Polling interval in milliseconds when the 80 + * link is down. 81 + * DP83TG720S_FAST_POLL_DURATION_MS - Timeout in milliseconds for no-link 82 + * polling after which polling interval is 83 + * increased. 84 + * DP83TG720S_POLL_SLOW - Slow polling interval when there is no 85 + * link for a prolongued period. 64 86 * DP83TG720S_RESET_DELAY_MS_MASTER - Delay after a reset before attempting 65 87 * to establish a link again for master phy. 66 88 * DP83TG720S_RESET_DELAY_MS_SLAVE - Delay after a reset before attempting ··· 93 71 * minimizing the number of reset retries while ensuring reliable link recovery 94 72 * within a reasonable timeframe. 95 73 */ 96 - #define DP83TG720S_POLL_ACTIVE_LINK 1000 97 - #define DP83TG720S_POLL_NO_LINK_MIN 100 98 - #define DP83TG720S_POLL_NO_LINK_MAX 1000 74 + #define DP83TG720S_POLL_ACTIVE_LINK 421 75 + #define DP83TG720S_POLL_NO_LINK 149 76 + #define DP83TG720S_FAST_POLL_DURATION_MS 6000 77 + #define DP83TG720S_POLL_SLOW 1117 99 78 #define DP83TG720S_RESET_DELAY_MS_MASTER 97 100 79 #define DP83TG720S_RESET_DELAY_MS_SLAVE 149 101 80 ··· 195 172 196 173 struct dp83tg720_priv { 197 174 struct dp83tg720_stats stats; 175 + unsigned long last_link_down_jiffies; 198 176 }; 199 177 200 178 /** ··· 599 575 } 600 576 601 577 /** 602 - * dp83tg720_get_next_update_time - Determine the next update time for PHY 603 - * state 578 + * dp83tg720_get_next_update_time - Return next polling interval for PHY state 604 579 * @phydev: Pointer to the phy_device structure 605 580 * 606 - * This function addresses a limitation of the DP83TG720 PHY, which cannot 607 - * reliably detect or report a stable link state. To recover from such 608 - * scenarios, the PHY must be periodically reset when the link is down. However, 609 - * if the link partner also runs Linux with the same driver, synchronized reset 610 - * intervals can lead to a deadlock where the link never establishes due to 611 - * simultaneous resets on both sides. 581 + * Implements adaptive polling interval logic depending on link state and 582 + * downtime duration. See the "2. Polling-Based Link Detection and IRQ Support" 583 + * section at the top of this file for details. 612 584 * 613 - * To avoid this, the function implements randomized polling intervals when the 614 - * link is down. It ensures that reset intervals are desynchronized by 615 - * introducing a random delay between a configured minimum and maximum range. 616 - * When the link is up, a fixed polling interval is used to minimize overhead. 617 - * 618 - * This mechanism guarantees that the link will reestablish within 10 seconds 619 - * in the worst-case scenario. 620 - * 621 - * Return: Time (in jiffies) until the next update event for the PHY state 622 - * machine. 585 + * Return: Time (in jiffies) until the next poll 623 586 */ 624 587 static unsigned int dp83tg720_get_next_update_time(struct phy_device *phydev) 625 588 { 589 + struct dp83tg720_priv *priv = phydev->priv; 626 590 unsigned int next_time_jiffies; 627 591 628 592 if (phydev->link) { 629 - /* When the link is up, use a fixed 1000ms interval 630 - * (in jiffies) 631 - */ 593 + priv->last_link_down_jiffies = 0; 594 + 595 + /* When the link is up, use a slower interval (in jiffies) */ 632 596 next_time_jiffies = 633 597 msecs_to_jiffies(DP83TG720S_POLL_ACTIVE_LINK); 634 598 } else { 635 - unsigned int min_jiffies, max_jiffies, rand_jiffies; 599 + unsigned long now = jiffies; 636 600 637 - /* When the link is down, randomize interval between min/max 638 - * (in jiffies) 639 - */ 640 - min_jiffies = msecs_to_jiffies(DP83TG720S_POLL_NO_LINK_MIN); 641 - max_jiffies = msecs_to_jiffies(DP83TG720S_POLL_NO_LINK_MAX); 601 + if (!priv->last_link_down_jiffies) 602 + priv->last_link_down_jiffies = now; 642 603 643 - rand_jiffies = min_jiffies + 644 - get_random_u32_below(max_jiffies - min_jiffies + 1); 645 - next_time_jiffies = rand_jiffies; 604 + if (time_before(now, priv->last_link_down_jiffies + 605 + msecs_to_jiffies(DP83TG720S_FAST_POLL_DURATION_MS))) { 606 + /* Link recently went down: fast polling */ 607 + next_time_jiffies = 608 + msecs_to_jiffies(DP83TG720S_POLL_NO_LINK); 609 + } else { 610 + /* Link has been down for a while: slow polling */ 611 + next_time_jiffies = 612 + msecs_to_jiffies(DP83TG720S_POLL_SLOW); 613 + } 646 614 } 647 615 648 616 /* Ensure the polling time is at least one jiffy */