18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci#include <linux/linkmode.h> 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci/** 58c2ecf20Sopenharmony_ci * linkmode_resolve_pause - resolve the allowable pause modes 68c2ecf20Sopenharmony_ci * @local_adv: local advertisement in ethtool format 78c2ecf20Sopenharmony_ci * @partner_adv: partner advertisement in ethtool format 88c2ecf20Sopenharmony_ci * @tx_pause: pointer to bool to indicate whether transmit pause should be 98c2ecf20Sopenharmony_ci * enabled. 108c2ecf20Sopenharmony_ci * @rx_pause: pointer to bool to indicate whether receive pause should be 118c2ecf20Sopenharmony_ci * enabled. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Flow control is resolved according to our and the link partners 148c2ecf20Sopenharmony_ci * advertisements using the following drawn from the 802.3 specs: 158c2ecf20Sopenharmony_ci * Local device Link partner 168c2ecf20Sopenharmony_ci * Pause AsymDir Pause AsymDir Result 178c2ecf20Sopenharmony_ci * 0 X 0 X Disabled 188c2ecf20Sopenharmony_ci * 0 1 1 0 Disabled 198c2ecf20Sopenharmony_ci * 0 1 1 1 TX 208c2ecf20Sopenharmony_ci * 1 0 0 X Disabled 218c2ecf20Sopenharmony_ci * 1 X 1 X TX+RX 228c2ecf20Sopenharmony_ci * 1 1 0 1 RX 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_civoid linkmode_resolve_pause(const unsigned long *local_adv, 258c2ecf20Sopenharmony_ci const unsigned long *partner_adv, 268c2ecf20Sopenharmony_ci bool *tx_pause, bool *rx_pause) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(m); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci linkmode_and(m, local_adv, partner_adv); 318c2ecf20Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { 328c2ecf20Sopenharmony_ci *tx_pause = true; 338c2ecf20Sopenharmony_ci *rx_pause = true; 348c2ecf20Sopenharmony_ci } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { 358c2ecf20Sopenharmony_ci *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 368c2ecf20Sopenharmony_ci partner_adv); 378c2ecf20Sopenharmony_ci *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 388c2ecf20Sopenharmony_ci local_adv); 398c2ecf20Sopenharmony_ci } else { 408c2ecf20Sopenharmony_ci *tx_pause = false; 418c2ecf20Sopenharmony_ci *rx_pause = false; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(linkmode_resolve_pause); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * linkmode_set_pause - set the pause mode advertisement 488c2ecf20Sopenharmony_ci * @advertisement: advertisement in ethtool format 498c2ecf20Sopenharmony_ci * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member 508c2ecf20Sopenharmony_ci * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * Configure the advertised Pause and Asym_Pause bits according to the 538c2ecf20Sopenharmony_ci * capabilities of provided in @tx and @rx. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * We convert as follows: 568c2ecf20Sopenharmony_ci * tx rx Pause AsymDir 578c2ecf20Sopenharmony_ci * 0 0 0 0 588c2ecf20Sopenharmony_ci * 0 1 1 1 598c2ecf20Sopenharmony_ci * 1 0 0 1 608c2ecf20Sopenharmony_ci * 1 1 1 0 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Note: this translation from ethtool tx/rx notation to the advertisement 638c2ecf20Sopenharmony_ci * is actually very problematical. Here are some examples: 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Local device Link partner 688c2ecf20Sopenharmony_ci * Pause AsymDir Pause AsymDir Result 698c2ecf20Sopenharmony_ci * 1 1 1 0 TX + RX - but we have no TX support. 708c2ecf20Sopenharmony_ci * 1 1 0 1 Only this gives RX only 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * For tx=1 rx=1, meaning we have the capability to transmit and receive 738c2ecf20Sopenharmony_ci * pause frames: 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Local device Link partner 768c2ecf20Sopenharmony_ci * Pause AsymDir Pause AsymDir Result 778c2ecf20Sopenharmony_ci * 1 0 0 1 Disabled - but since we do support tx and rx, 788c2ecf20Sopenharmony_ci * this should resolve to RX only. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Hence, asking for: 818c2ecf20Sopenharmony_ci * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up 828c2ecf20Sopenharmony_ci * resolving to tx+rx pause or only rx pause depending on 838c2ecf20Sopenharmony_ci * the partners advertisement. 848c2ecf20Sopenharmony_ci * rx=0 tx=1 gives AsymDir only, which will only give tx pause if 858c2ecf20Sopenharmony_ci * the partners advertisement allows it. 868c2ecf20Sopenharmony_ci * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause 878c2ecf20Sopenharmony_ci * if the other end also advertises Pause. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_civoid linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); 928c2ecf20Sopenharmony_ci linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, 938c2ecf20Sopenharmony_ci rx ^ tx); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(linkmode_set_pause); 96