[RFC PATCH v2 net-next 06/15] net: mii: add C73 base page helpers

From: Vladimir Oltean
Date: Sat Sep 23 2023 - 09:49:53 EST


IEEE 802.3 clause 73 defines auto-negotiation for backplanes and copper
cable assemblies. This is a medium type (link mode) just like Ethernet
over twisted pairs (BASE-T / BASE-TX) and over two wires for automotive
(BASE-T1) for which the PHY library currently contains support.

As a minimal framework for backplane PHY drivers, introduce a set of
helpers that parse and interpret the base pages that are exchanged by
PHYs during the clause 73 negotiation.

The placement in the "legacy" mii code is perhaps not the best, but I
tried to put them somewhere accessible by phylib, phylink and non-phylib
drivers. Note that phylink also has its own phylink_resolve_c73() which
is more or less similar in purpose, but:

- it requires constructing a struct phylink_link_state which is deeply
embedded with the phylink API and that may not be desirable for
drivers

- the presence of some link modes in phylink's own
phylink_c73_priority_resolution[] is "interesting", like
ETHTOOL_LINK_MODE_2500baseX_Full_BIT, which is not a backplane mode
negotiable through C73 at all. That comes from the xpcs driver which
may have a non-standard C73 autoneg, and this makes it difficult for
me to e.g. refactor phylink_resolve_c73() to use the more generic
linkmode_c73_priority_resolution(). Also see the attached link where I
had previously pointed this out.

Link: https://lore.kernel.org/netdev/20230516090009.ssq3uedjl53kzsjr@skbuf/
Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
---
v1->v2: add 25GBase-KR-S and 25GBase-CR-S

drivers/net/mii.c | 34 +++++++++++++-
include/linux/mii.h | 105 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/drivers/net/mii.c b/drivers/net/mii.c
index 22680f47385d..03e8b0877600 100644
--- a/drivers/net/mii.c
+++ b/drivers/net/mii.c
@@ -648,6 +648,38 @@ int generic_mii_ioctl(struct mii_if_info *mii_if,
return rc;
}

+static const enum ethtool_link_mode_bit_indices c73_linkmodes[] = {
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ /* ETHTOOL_LINK_MODE_100000baseKP4_Full_BIT not supported */
+ /* ETHTOOL_LINK_MODE_100000baseCR10_Full_BIT not supported */
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_S_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseCR_S_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+};
+
+int
+linkmode_c73_priority_resolution(const unsigned long *modes,
+ enum ethtool_link_mode_bit_indices *resolved)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(c73_linkmodes); i++) {
+ if (linkmode_test_bit(c73_linkmodes[i], modes)) {
+ *resolved = c73_linkmodes[i];
+ return 0;
+ }
+ }
+
+ return -ENOPROTOOPT;
+}
+
MODULE_AUTHOR ("Jeff Garzik <jgarzik@xxxxxxxxx>");
MODULE_DESCRIPTION ("MII hardware support library");
MODULE_LICENSE("GPL");
@@ -662,4 +694,4 @@ EXPORT_SYMBOL(mii_check_link);
EXPORT_SYMBOL(mii_check_media);
EXPORT_SYMBOL(mii_check_gmii_support);
EXPORT_SYMBOL(generic_mii_ioctl);
-
+EXPORT_SYMBOL(linkmode_c73_priority_resolution);
diff --git a/include/linux/mii.h b/include/linux/mii.h
index d5a959ce4877..4b141e9acd08 100644
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -13,6 +13,36 @@
#include <linux/linkmode.h>
#include <uapi/linux/mii.h>

+/* 802.3-2018 clause 73.6 Link codeword encoding */
+#define C73_BASE_PAGE_SELECTOR(x) ((x) & GENMASK(4, 0))
+#define C73_BASE_PAGE_ECHOED_NONCE(x) (((x) << 5) & GENMASK(9, 5))
+#define C73_BASE_PAGE_ECHOED_NONCE_X(x) (((x) & GENMASK(9, 5)) >> 5)
+#define C73_BASE_PAGE_ECHOED_NONCE_MSK GENMASK(9, 5)
+#define C73_BASE_PAGE_PAUSE BIT(10)
+#define C73_BASE_PAGE_ASM_DIR BIT(11)
+#define C73_BASE_PAGE_RF BIT(13)
+#define C73_BASE_PAGE_ACK BIT(14)
+#define C73_BASE_PAGE_NP BIT(15)
+#define C73_BASE_PAGE_TRANSMITTED_NONCE(x) (((x) << 16) & GENMASK(20, 16))
+#define C73_BASE_PAGE_TRANSMITTED_NONCE_X(x) (((x) & GENMASK(20, 16)) >> 16)
+#define C73_BASE_PAGE_TRANSMITTED_NONCE_MSK GENMASK(20, 16)
+#define C73_BASE_PAGE_A(x) BIT(21 + (x))
+#define C73_BASE_PAGE_TECH_ABL_1000BASEKX C73_BASE_PAGE_A(0)
+#define C73_BASE_PAGE_TECH_ABL_10GBASEKX4 C73_BASE_PAGE_A(1)
+#define C73_BASE_PAGE_TECH_ABL_10GBASEKR C73_BASE_PAGE_A(2)
+#define C73_BASE_PAGE_TECH_ABL_40GBASEKR4 C73_BASE_PAGE_A(3)
+#define C73_BASE_PAGE_TECH_ABL_40GBASECR4 C73_BASE_PAGE_A(4)
+#define C73_BASE_PAGE_TECH_ABL_100GBASECR10 C73_BASE_PAGE_A(5)
+#define C73_BASE_PAGE_TECH_ABL_100GBASEKP4 C73_BASE_PAGE_A(6)
+#define C73_BASE_PAGE_TECH_ABL_100GBASEKR4 C73_BASE_PAGE_A(7)
+#define C73_BASE_PAGE_TECH_ABL_100GBASECR4 C73_BASE_PAGE_A(8)
+#define C73_BASE_PAGE_TECH_ABL_25GBASEKRS C73_BASE_PAGE_A(9)
+#define C73_BASE_PAGE_TECH_ABL_25GBASEKR C73_BASE_PAGE_A(10)
+#define C73_BASE_PAGE_25G_RS_FEC_REQ BIT_ULL(44)
+#define C73_BASE_PAGE_25G_BASER_FEC_REQ BIT_ULL(45)
+#define C73_BASE_PAGE_10G_BASER_FEC_ABL BIT_ULL(46)
+#define C73_BASE_PAGE_10G_BASER_FEC_REQ BIT_ULL(47)
+
struct ethtool_cmd;

struct mii_if_info {
@@ -47,6 +77,10 @@ extern int generic_mii_ioctl(struct mii_if_info *mii_if,
struct mii_ioctl_data *mii_data, int cmd,
unsigned int *duplex_changed);

+extern int
+linkmode_c73_priority_resolution(const unsigned long *modes,
+ enum ethtool_link_mode_bit_indices *resolved);
+

static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
{
@@ -506,6 +540,77 @@ static inline u16 linkmode_adv_to_mii_adv_x(const unsigned long *linkmodes,
return adv;
}

+static inline u64 linkmode_adv_to_c73_base_page(const unsigned long *advertising)
+{
+ u64 result = 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_1000BASEKX;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_10GBASEKX4;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_10GBASEKR;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_40GBASEKR4;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_40GBASECR4;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_100GBASEKR4;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_100GBASECR4;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ advertising))
+ result |= C73_BASE_PAGE_TECH_ABL_25GBASEKR;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising))
+ result |= C73_BASE_PAGE_PAUSE;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising))
+ result |= C73_BASE_PAGE_ASM_DIR;
+
+ return result;
+}
+
+static inline void mii_c73_mod_linkmode_lpa_t(unsigned long *advertising,
+ u64 base_page)
+{
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_1000BASEKX);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_10GBASEKX4);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_10GBASEKR);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_40GBASEKR4);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_40GBASECR4);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_100GBASEKR4);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_100GBASECR4);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, advertising,
+ base_page & C73_BASE_PAGE_TECH_ABL_25GBASEKR);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising,
+ base_page & C73_BASE_PAGE_PAUSE);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising,
+ base_page & C73_BASE_PAGE_ASM_DIR);
+}
+
/**
* mii_advertise_flowctrl - get flow control advertisement flags
* @cap: Flow control capabilities (FLOW_CTRL_RX, FLOW_CTRL_TX or both)
--
2.34.1