net: add helpers to resolve negotiated flow control
[linux-2.6-microblaze.git] / drivers / net / phy / linkmode.c
1 // SPDX-License-Identifier: GPL-2.0+
2 #include <linux/linkmode.h>
3
4 /**
5  * linkmode_resolve_pause - resolve the allowable pause modes
6  * @local_adv: local advertisement in ethtool format
7  * @partner_adv: partner advertisement in ethtool format
8  * @tx_pause: pointer to bool to indicate whether transmit pause should be
9  * enabled.
10  * @rx_pause: pointer to bool to indicate whether receive pause should be
11  * enabled.
12  *
13  * Flow control is resolved according to our and the link partners
14  * advertisements using the following drawn from the 802.3 specs:
15  *  Local device  Link partner
16  *  Pause AsymDir Pause AsymDir Result
17  *    0     X       0     X     Disabled
18  *    0     1       1     0     Disabled
19  *    0     1       1     1     TX
20  *    1     0       0     X     Disabled
21  *    1     X       1     X     TX+RX
22  *    1     1       0     1     RX
23  */
24 void linkmode_resolve_pause(const unsigned long *local_adv,
25                             const unsigned long *partner_adv,
26                             bool *tx_pause, bool *rx_pause)
27 {
28         __ETHTOOL_DECLARE_LINK_MODE_MASK(m);
29
30         linkmode_and(m, local_adv, partner_adv);
31         if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) {
32                 *tx_pause = true;
33                 *rx_pause = true;
34         } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) {
35                 *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
36                                               partner_adv);
37                 *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
38                                               local_adv);
39         } else {
40                 *tx_pause = false;
41                 *rx_pause = false;
42         }
43 }
44 EXPORT_SYMBOL_GPL(linkmode_resolve_pause);