From: Doug Ledford <doug.ledford@hpe.com>
To: "Slingshot Open Source Team"
<slingshot-os-team@groups.ext.hpe.com>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nicolas Schier" <nsc@kernel.org>,
"Richard Cochran" <richardcochran@gmail.com>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Guenter Roeck" <linux@roeck-us.net>
Cc: <linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
<linux-kbuild@vger.kernel.org>,
Doug Ledford <doug.ledford@hpe.com>,
"Sandeep Bhoot" <sandeep.bhoot@hpe.com>
Subject: [PATCH 10/23] net: hpe: sl: add the module entry points
Date: Fri, 30 Oct 2026 13:59:06 -0700 [thread overview]
Message-ID: <20261030-cxi-netdev-submission-v1-10-8c2c391cc283@hpe.com> (raw)
In-Reply-To: <20261030-cxi-netdev-submission-v1-0-8c2c391cc283@hpe.com>
Add the module entry points and the logical device, link group, link,
MAC, LLR, FEC and media objects that make up the SL API used by the
NIC driver.
checkpatch --strict (100 columns) still reports the following in this
patch. They need coordination with the internal repositories this code
is maintained in and will be fixed in V2:
- WARNING: LONG_LINE_STRING (17), LONG_LINE (6)
- CHECK: BRACES (2), UNNECESSARY_PARENTHESES (2)
Co-developed-by: Sandeep Bhoot <sandeep.bhoot@hpe.com>
Signed-off-by: Sandeep Bhoot <sandeep.bhoot@hpe.com>
Signed-off-by: Doug Ledford <doug.ledford@hpe.com>
---
drivers/net/ethernet/hpe/sl/sl_fec.c | 68 ++++
drivers/net/ethernet/hpe/sl/sl_ldev.c | 133 ++++++
drivers/net/ethernet/hpe/sl/sl_lgrp.c | 409 +++++++++++++++++++
drivers/net/ethernet/hpe/sl/sl_link.c | 632 +++++++++++++++++++++++++++++
drivers/net/ethernet/hpe/sl/sl_llr.c | 289 +++++++++++++
drivers/net/ethernet/hpe/sl/sl_mac.c | 220 ++++++++++
drivers/net/ethernet/hpe/sl/sl_media.c | 356 ++++++++++++++++
drivers/net/ethernet/hpe/sl/sl_media_nic.c | 23 ++
drivers/net/ethernet/hpe/sl/sl_module.c | 129 ++++++
9 files changed, 2259 insertions(+)
diff --git a/drivers/net/ethernet/hpe/sl/sl_fec.c b/drivers/net/ethernet/hpe/sl/sl_fec.c
new file mode 100644
index 000000000000..af438c9443f7
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_fec.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+
+#include <linux/hpe/sl/sl_fec.h>
+
+#include "sl_log.h"
+#include "sl_link.h"
+#include "sl_ctrl_link_fec.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_FEC_LOG_NAME
+
+int sl_fec_info_get(struct sl_link *link, struct sl_fec_info *fec_info)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "fec info get fail");
+ return rtn;
+ }
+ if (!fec_info) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL fec_info");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_link_fec_info_get(link->ldev_num, link->lgrp_num, link->num, fec_info);
+}
+EXPORT_SYMBOL(sl_fec_info_get);
+
+int sl_fec_tail_get(struct sl_link *link, struct sl_fec_tail *fec_tail)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "fec tail get fail");
+ return rtn;
+ }
+ if (!fec_tail) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL fec_tail");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_link_fec_tail_get(link->ldev_num, link->lgrp_num, link->num, fec_tail);
+}
+EXPORT_SYMBOL(sl_fec_tail_get);
+
+int sl_fec_ber_calc(struct sl_fec_info *fec_info, struct sl_ber *ucw_ber, struct sl_ber *ccw_ber)
+{
+ if (!fec_info) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL fec_info");
+ return -EINVAL;
+ }
+ if (!ucw_ber) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL ucw_ber");
+ return -EINVAL;
+ }
+ if (!ccw_ber) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL ccw_ber");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_link_fec_ber_calc(fec_info, ucw_ber, ccw_ber);
+}
+EXPORT_SYMBOL(sl_fec_ber_calc);
diff --git a/drivers/net/ethernet/hpe/sl/sl_ldev.c b/drivers/net/ethernet/hpe/sl/sl_ldev.c
new file mode 100644
index 000000000000..631d215fb7f4
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_ldev.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+
+#include <linux/hpe/sl/sl_ldev.h>
+
+#include "sl_asic.h"
+#include "sl_log.h"
+#include "sl_ldev.h"
+#include "sl_sysfs.h"
+#include "sl_ctrl_ldev.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_LDEV_LOG_NAME
+
+static struct sl_ldev ldevs[SL_ASIC_MAX_LDEVS];
+
+void sl_ldev_init(void)
+{
+ u8 ldev_num;
+
+ for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) {
+ ldevs[ldev_num].magic = SL_LDEV_MAGIC;
+ ldevs[ldev_num].ver = SL_LDEV_VER;
+ ldevs[ldev_num].size = sizeof(struct sl_ldev);
+ ldevs[ldev_num].num = ldev_num;
+ }
+}
+
+int sl_ldev_check(struct sl_ldev *ldev)
+{
+ if (!ldev) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL ldev");
+ return -EINVAL;
+ }
+ if (IS_ERR(ldev)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "ldev pointer error");
+ return -EINVAL;
+ }
+ if (ldev->magic != SL_LDEV_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev magic");
+ return -EINVAL;
+ }
+ if (ldev->ver != SL_LDEV_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev version");
+ return -EINVAL;
+ }
+ if (ldev->num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev num");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct sl_ldev *sl_ldev_new(u8 ldev_num,
+ struct workqueue_struct *workq, struct sl_ldev_attr *ldev_attr)
+{
+ int rtn;
+
+ if (ldev_num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "invalid (ldev_num = %u)", ldev_num);
+ return ERR_PTR(-EINVAL);
+ }
+ rtn = sl_ldev_attr_check(ldev_attr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ rtn = sl_ctrl_ldev_new(ldev_num, workq, ldev_attr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ return &ldevs[ldev_num];
+}
+EXPORT_SYMBOL(sl_ldev_new);
+
+int sl_ldev_sysfs_parent_set(struct sl_ldev *ldev, struct kobject *parent)
+{
+ int rtn;
+
+ rtn = sl_ldev_check(ldev);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "parent set fail");
+ return rtn;
+ }
+ if (!parent) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL parent");
+ return -EINVAL;
+ }
+
+ return sl_sysfs_ldev_create(ldev->num, parent);
+}
+EXPORT_SYMBOL(sl_ldev_sysfs_parent_set);
+
+int sl_ldev_serdes_init(struct sl_ldev *ldev)
+{
+ int rtn;
+
+ rtn = sl_ldev_check(ldev);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "serdes init fail");
+ return rtn;
+ }
+
+ return sl_ctrl_ldev_serdes_init(ldev->num);
+}
+EXPORT_SYMBOL(sl_ldev_serdes_init);
+
+int sl_ldev_del(struct sl_ldev *ldev)
+{
+ int rtn;
+
+ rtn = sl_ldev_check(ldev);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "del fail");
+ return rtn;
+ }
+
+ return sl_ctrl_ldev_del(ldev->num);
+}
+EXPORT_SYMBOL(sl_ldev_del);
+
+void sl_ldev_exit(void)
+{
+ sl_ctrl_ldev_exit();
+}
diff --git a/drivers/net/ethernet/hpe/sl/sl_lgrp.c b/drivers/net/ethernet/hpe/sl/sl_lgrp.c
new file mode 100644
index 000000000000..bb2dc68f4198
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_lgrp.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023-2026 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/hpe/sl/sl_lgrp.h>
+
+#include "sl_asic.h"
+#include "sl_log.h"
+#include "sl_ldev.h"
+#include "sl_lgrp.h"
+#include "sl_core_lgrp.h"
+#include "sl_core_link.h"
+#include "sl_ctrl_lgrp.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_LGRP_LOG_NAME
+
+static struct sl_lgrp lgrps[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS];
+
+void sl_lgrp_init(void)
+{
+ u8 ldev_num;
+ u8 lgrp_num;
+
+ for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) {
+ for (lgrp_num = 0; lgrp_num < SL_ASIC_MAX_LGRPS; ++lgrp_num) {
+ lgrps[ldev_num][lgrp_num].magic = SL_LGRP_MAGIC;
+ lgrps[ldev_num][lgrp_num].ver = SL_LGRP_VER;
+ lgrps[ldev_num][lgrp_num].num = lgrp_num;
+ lgrps[ldev_num][lgrp_num].ldev_num = ldev_num;
+ }
+ }
+}
+
+int sl_lgrp_check(struct sl_lgrp *lgrp)
+{
+ if (!lgrp) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL lgrp");
+ return -EINVAL;
+ }
+ if (IS_ERR(lgrp)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "lgrp pointer error");
+ return -EINVAL;
+ }
+ if (lgrp->magic != SL_LGRP_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp magic");
+ return -EINVAL;
+ }
+ if (lgrp->ver != SL_LGRP_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp version");
+ return -EINVAL;
+ }
+ if (lgrp->num >= SL_ASIC_MAX_LGRPS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp num");
+ return -EINVAL;
+ }
+ if (lgrp->ldev_num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev num");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_lgrp_config_check(struct sl_lgrp_config *lgrp_config)
+{
+ if (!lgrp_config) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL lgrp_config");
+ return -EINVAL;
+ }
+ if (IS_ERR(lgrp_config)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "lgrp_config pointer error");
+ return -EINVAL;
+ }
+ if (lgrp_config->magic != SL_LGRP_CONFIG_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp_config magic");
+ return -EINVAL;
+ }
+ if (lgrp_config->ver != SL_LGRP_CONFIG_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp_config version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_lgrp_policy_check(struct sl_lgrp_policy *lgrp_policy)
+{
+ if (!lgrp_policy) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL lgrp_policy");
+ return -EINVAL;
+ }
+ if (IS_ERR(lgrp_policy)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "lgrp_policy pointer error");
+ return -EINVAL;
+ }
+ if (lgrp_policy->magic != SL_LGRP_POLICY_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp_policy magic");
+ return -EINVAL;
+ }
+ if (lgrp_policy->ver != SL_LGRP_POLICY_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp_policy version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int sl_lgrp_hw_attr_set(struct sl_lgrp *lgrp, struct sl_hw_attr *hw_attr)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "hw attr set fail");
+ return rtn;
+ }
+ if (!hw_attr) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL hw_attr");
+ return -EINVAL;
+ }
+
+ return sl_core_lgrp_hw_attr_set(lgrp->ldev_num, lgrp->num, hw_attr);
+}
+EXPORT_SYMBOL(sl_lgrp_hw_attr_set);
+
+struct sl_lgrp *sl_lgrp_new(struct sl_ldev *ldev, u8 lgrp_num, struct kobject *sysfs_parent)
+{
+ int rtn;
+
+ rtn = sl_ldev_check(ldev);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+ if (lgrp_num >= SL_ASIC_MAX_LGRPS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "invalid (lgrp_num = %u)", lgrp_num);
+ return ERR_PTR(-EINVAL);
+ }
+
+ rtn = sl_ctrl_lgrp_new(ldev->num, lgrp_num, sysfs_parent);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ return &lgrps[ldev->num][lgrp_num];
+}
+EXPORT_SYMBOL(sl_lgrp_new);
+
+int sl_lgrp_del(struct sl_lgrp *lgrp)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "del fail");
+ return rtn;
+ }
+
+ return sl_ctrl_lgrp_del(lgrp->ldev_num, lgrp->num);
+}
+EXPORT_SYMBOL(sl_lgrp_del);
+
+int sl_lgrp_config_set(struct sl_lgrp *lgrp, struct sl_lgrp_config *lgrp_config)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "config set fail");
+ return rtn;
+ }
+ rtn = sl_lgrp_config_check(lgrp_config);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "config set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_lgrp_config_set(lgrp->ldev_num, lgrp->num, lgrp_config);
+}
+EXPORT_SYMBOL(sl_lgrp_config_set);
+
+int sl_lgrp_policy_set(struct sl_lgrp *lgrp, struct sl_lgrp_policy *lgrp_policy)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+ rtn = sl_lgrp_policy_check(lgrp_policy);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_lgrp_policy_set(lgrp->ldev_num, lgrp->num, lgrp_policy);
+}
+EXPORT_SYMBOL(sl_lgrp_policy_set);
+
+int sl_lgrp_notif_callback_reg(struct sl_lgrp *lgrp, sl_lgrp_notif_t callback,
+ u32 types, void *tag)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "callback reg fail");
+ return rtn;
+ }
+ if (!callback) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL callback");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_lgrp_notif_callback_reg(lgrp->ldev_num, lgrp->num, callback, types, tag);
+}
+EXPORT_SYMBOL(sl_lgrp_notif_callback_reg);
+
+int sl_lgrp_notif_callback_unreg(struct sl_lgrp *lgrp, sl_lgrp_notif_t callback,
+ u32 types)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "callback unreg fail");
+ return rtn;
+ }
+ if (!callback) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL callback");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_lgrp_notif_callback_unreg(lgrp->ldev_num, lgrp->num, callback, types);
+}
+EXPORT_SYMBOL(sl_lgrp_notif_callback_unreg);
+
+int sl_lgrp_connect_id_set(struct sl_lgrp *lgrp, const char *connect_id)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "id set fail");
+ return rtn;
+ }
+ if (!connect_id) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL connect_id");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_lgrp_connect_id_set(lgrp->ldev_num, lgrp->num, connect_id);
+}
+EXPORT_SYMBOL(sl_lgrp_connect_id_set);
+
+const char *sl_lgrp_config_tech_str(u32 config)
+{
+ if (config == SL_LGRP_CONFIG_TECH_CK_400G)
+ return "ck400G";
+ if (config == SL_LGRP_CONFIG_TECH_CK_200G)
+ return "ck200G";
+ if (config == SL_LGRP_CONFIG_TECH_CK_100G)
+ return "ck100G";
+ if (config == SL_LGRP_CONFIG_TECH_BS_200G)
+ return "bs200G";
+ if (config == SL_LGRP_CONFIG_TECH_CD_100G)
+ return "cd100G";
+ if (config == SL_LGRP_CONFIG_TECH_CD_50G)
+ return "cd50G";
+ if (config == SL_LGRP_CONFIG_TECH_BJ_100G)
+ return "bj100G";
+
+ return "unknown";
+}
+EXPORT_SYMBOL(sl_lgrp_config_tech_str);
+
+const char *sl_lgrp_config_fec_str(u32 config)
+{
+ if (config == SL_LGRP_CONFIG_FEC_RS_LL)
+ return "reed-solomon-low-latency";
+ if (config == SL_LGRP_CONFIG_FEC_RS)
+ return "reed-solomon";
+
+ return "unknown";
+}
+EXPORT_SYMBOL(sl_lgrp_config_fec_str);
+
+const char *sl_lgrp_furcation_str(u32 furcation)
+{
+ if (furcation == SL_MEDIA_FURCATION_X1)
+ return "unfurcated";
+ if (furcation == SL_MEDIA_FURCATION_X2)
+ return "bifurcated";
+ if (furcation == SL_MEDIA_FURCATION_X4)
+ return "quadfurcated";
+
+ return "unknown";
+}
+EXPORT_SYMBOL(sl_lgrp_furcation_str);
+
+const char *sl_lgrp_notif_str(u32 notif)
+{
+ switch (notif) {
+ case SL_LGRP_NOTIF_INVALID:
+ return "invalid";
+ case SL_LGRP_NOTIF_LINK_UP:
+ return "link-up";
+ case SL_LGRP_NOTIF_LINK_UP_FAIL:
+ return "link-up-fail";
+ case SL_LGRP_NOTIF_LINK_DOWN:
+ return "link-down";
+ case SL_LGRP_NOTIF_LINK_ASYNC_DOWN:
+ return "link-async-down";
+ case SL_LGRP_NOTIF_LINK_ERROR:
+ return "link-error";
+ case SL_LGRP_NOTIF_LINK_UCW_WARN:
+ return "link-ucw-warn";
+ case SL_LGRP_NOTIF_LINK_CCW_WARN:
+ return "link-ccw-warn";
+ case SL_LGRP_NOTIF_LLR_SETUP:
+ return "llr-setup";
+ case SL_LGRP_NOTIF_LLR_SETUP_TIMEOUT:
+ return "llr-setup-timeout";
+ case SL_LGRP_NOTIF_LLR_RUNNING:
+ return "llr-running";
+ case SL_LGRP_NOTIF_LLR_START_TIMEOUT:
+ return "llr-start-timeout";
+ case SL_LGRP_NOTIF_LLR_CANCELED:
+ return "llr-canceled";
+ case SL_LGRP_NOTIF_LLR_ERROR:
+ return "llr-error";
+ case SL_LGRP_NOTIF_MEDIA_PRESENT:
+ return "media-present";
+ case SL_LGRP_NOTIF_MEDIA_NOT_PRESENT:
+ return "media-not-present";
+ case SL_LGRP_NOTIF_MEDIA_ERROR:
+ return "media-error";
+ case SL_LGRP_NOTIF_MEDIA_HOT:
+ return "media-hot";
+ case SL_LGRP_NOTIF_MEDIA_COLD:
+ return "media-cold";
+ case SL_LGRP_NOTIF_MEDIA_WARM:
+ return "media-warm";
+ case SL_LGRP_NOTIF_AN_DATA:
+ return "an-data";
+ case SL_LGRP_NOTIF_AN_TIMEOUT:
+ return "an-timeout";
+ case SL_LGRP_NOTIF_AN_ERROR:
+ return "an-error";
+ case SL_LGRP_NOTIF_LANE_DEGRADE:
+ return "auto-lane-degrade";
+ case SL_LGRP_NOTIF_LANE_DEGRADE_RECOVERY:
+ return "auto-lane-degrade-recovery";
+ case SL_LGRP_NOTIF_PML_RECOVERY:
+ return "pml-recovery";
+ case SL_LGRP_NOTIF_LINK_DOWN_REQ:
+ return "link-down-request";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_lgrp_notif_str);
+
+const char *sl_lgrp_config_opt_str(u32 option)
+{
+ switch (option) {
+ case SL_LGRP_CONFIG_OPT_SERDES_LOOPBACK_ENABLE:
+ return "loopback-serdes-enable";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_lgrp_config_opt_str);
+
+const char *sl_lgrp_fec_mode_str(u32 mode)
+{
+ switch (mode) {
+ case SL_LGRP_FEC_MODE_OFF:
+ return "off";
+ case SL_LGRP_FEC_MODE_MONITOR:
+ return "monitor";
+ case SL_LGRP_FEC_MODE_CORRECT:
+ return "correct";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_lgrp_fec_mode_str);
+
+const char *sl_lgrp_llr_mode_str(u32 mode)
+{
+ switch (mode) {
+ case SL_LGRP_LLR_MODE_OFF:
+ return "off";
+ case SL_LGRP_LLR_MODE_MONITOR:
+ return "monitor";
+ case SL_LGRP_LLR_MODE_ON:
+ return "on";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_lgrp_llr_mode_str);
diff --git a/drivers/net/ethernet/hpe/sl/sl_link.c b/drivers/net/ethernet/hpe/sl/sl_link.c
new file mode 100644
index 000000000000..c66e48a62748
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_link.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023-2026 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <linux/hpe/sl/sl_link.h>
+
+#include "sl_asic.h"
+#include "sl_log.h"
+#include "sl_lgrp.h"
+#include "sl_link.h"
+#include "sl_core_str.h"
+#include "sl_ctrl_link.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_LINK_LOG_NAME
+
+static struct sl_link links[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS];
+
+void sl_link_init(void)
+{
+ u8 ldev_num;
+ u8 lgrp_num;
+ u8 link_num;
+
+ for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) {
+ for (lgrp_num = 0; lgrp_num < SL_ASIC_MAX_LGRPS; ++lgrp_num) {
+ for (link_num = 0; link_num < SL_ASIC_MAX_LINKS; ++link_num) {
+ links[ldev_num][lgrp_num][link_num].magic = SL_LINK_MAGIC;
+ links[ldev_num][lgrp_num][link_num].ver = SL_LINK_VER;
+ links[ldev_num][lgrp_num][link_num].num = link_num;
+ links[ldev_num][lgrp_num][link_num].lgrp_num = lgrp_num;
+ links[ldev_num][lgrp_num][link_num].ldev_num = ldev_num;
+ }
+ }
+ }
+}
+
+int sl_link_check(struct sl_link *link)
+{
+ if (!link) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL link");
+ return -EINVAL;
+ }
+ if (IS_ERR(link)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link pointer error");
+ return -EINVAL;
+ }
+ if (link->magic != SL_LINK_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link magic");
+ return -EINVAL;
+ }
+ if (link->ver != SL_LINK_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link version");
+ return -EINVAL;
+ }
+ if (link->num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link num");
+ return -EINVAL;
+ }
+ if (link->lgrp_num >= SL_ASIC_MAX_LGRPS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp num");
+ return -EINVAL;
+ }
+ if (link->ldev_num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev num");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_link_config_check(struct sl_link_config *link_config)
+{
+ if (!link_config) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL link_config");
+ return -EINVAL;
+ }
+ if (IS_ERR(link_config)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link_config pointer error");
+ return -EINVAL;
+ }
+ if (link_config->magic != SL_LINK_CONFIG_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link_config magic");
+ return -EINVAL;
+ }
+ if (link_config->ver != SL_LINK_CONFIG_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link_config version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_link_policy_check(struct sl_link_policy *link_policy)
+{
+ if (!link_policy) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL link_policy");
+ return -EINVAL;
+ }
+ if (IS_ERR(link_policy)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link_policy pointer error");
+ return -EINVAL;
+ }
+ if (link_policy->magic != SL_LINK_POLICY_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link_policy magic");
+ return -EINVAL;
+ }
+ if (link_policy->ver != SL_LINK_POLICY_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad link_policy version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct sl_link *sl_link_new(struct sl_lgrp *lgrp, u8 link_num, struct kobject *sysfs_parent)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+ if (link_num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "invalid (link_num = %u)", link_num);
+ return ERR_PTR(-EINVAL);
+ }
+
+ rtn = sl_ctrl_link_new(lgrp->ldev_num, lgrp->num, link_num, sysfs_parent);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ return &links[lgrp->ldev_num][lgrp->num][link_num];
+}
+EXPORT_SYMBOL(sl_link_new);
+
+int sl_link_del(struct sl_link *link)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "del fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_del(link->ldev_num, link->lgrp_num, link->num);
+}
+EXPORT_SYMBOL(sl_link_del);
+
+int sl_link_config_set(struct sl_link *link, struct sl_link_config *link_config)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link config set fail");
+ return rtn;
+ }
+ rtn = sl_link_config_check(link_config);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link config set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_config_set(link->ldev_num, link->lgrp_num, link->num, link_config);
+}
+EXPORT_SYMBOL(sl_link_config_set);
+
+int sl_link_policy_set(struct sl_link *link, struct sl_link_policy *link_policy)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+ rtn = sl_link_policy_check(link_policy);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_policy_set(link->ldev_num, link->lgrp_num, link->num, link_policy);
+}
+EXPORT_SYMBOL(sl_link_policy_set);
+
+int sl_link_an_lp_caps_get(struct sl_link *link, struct sl_link_caps *caps, u32 timeout_ms, u32 flags)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "lp caps get fail");
+ return rtn;
+ }
+ if (!caps) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL caps");
+ return -EINVAL;
+ }
+ if (timeout_ms == 0) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "0 timeout");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_link_an_lp_caps_get(link->ldev_num, link->lgrp_num, link->num, caps, timeout_ms, flags);
+}
+EXPORT_SYMBOL(sl_link_an_lp_caps_get);
+
+int sl_link_an_lp_caps_stop(struct sl_link *link)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "lp caps stop fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_an_lp_caps_stop(link->ldev_num, link->lgrp_num, link->num);
+}
+EXPORT_SYMBOL(sl_link_an_lp_caps_stop);
+
+int sl_link_up(struct sl_link *link)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link up fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_up(link->ldev_num, link->lgrp_num, link->num);
+}
+EXPORT_SYMBOL(sl_link_up);
+
+int sl_link_down(struct sl_link *link)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link down fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_down(link->ldev_num, link->lgrp_num, link->num);
+}
+EXPORT_SYMBOL(sl_link_down);
+
+int sl_link_reset(struct sl_link *link)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link reset fail");
+ return rtn;
+ }
+
+ return sl_ctrl_link_reset(link->ldev_num, link->lgrp_num, link->num);
+}
+EXPORT_SYMBOL(sl_link_reset);
+
+int sl_link_clocks_get(struct sl_link *link, u32 *up_count, s64 *up_time, s64 *total_time)
+{
+ int rtn;
+ s64 up;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link clocks get fail");
+ return rtn;
+ }
+
+ rtn = sl_ctrl_link_up_clocks_get(link->ldev_num, link->lgrp_num, link->num, up_time, total_time, &up);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link_up_clocks_get failed [%d]", rtn);
+ return rtn;
+ }
+
+ rtn = sl_ctrl_link_up_count_get(link->ldev_num, link->lgrp_num, link->num, up_count);
+ if (rtn)
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link_up_count_get failed [%d]", rtn);
+
+ return rtn;
+}
+EXPORT_SYMBOL(sl_link_clocks_get);
+
+int sl_link_state_get(struct sl_link *link, u32 *state)
+{
+ int rtn;
+
+ rtn = sl_link_check(link);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "link state get fail");
+ return rtn;
+ }
+ if (!state) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL state");
+ return -EINVAL;
+ }
+
+ return sl_ctrl_link_state_get_cmd(link->ldev_num, link->lgrp_num, link->num, state);
+}
+EXPORT_SYMBOL(sl_link_state_get);
+
+const char *sl_link_state_str(u32 state)
+{
+ switch (state) {
+ case SL_LINK_STATE_INVALID:
+ return "invalid";
+ case SL_LINK_STATE_DOWN:
+ return "down";
+ case SL_LINK_STATE_AN:
+ return "autoneg";
+ case SL_LINK_STATE_STARTING:
+ return "starting";
+ case SL_LINK_STATE_UP:
+ return "up";
+ case SL_LINK_STATE_UP_DOWN_REQ:
+ return "up-requested-down";
+ case SL_LINK_STATE_STOPPING:
+ return "stopping";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_link_state_str);
+
+const char *sl_link_degrade_state_str(u32 degrade_state)
+{
+ switch (degrade_state) {
+ case SL_LINK_DEGRADE_STATE_ACTIVE:
+ return "active";
+ case SL_LINK_DEGRADE_STATE_INACTIVE:
+ return "inactive";
+ case SL_LINK_DEGRADE_STATE_FAILED:
+ return "error";
+ case SL_LINK_DEGRADE_STATE_INVALID:
+ return "invalid";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_link_degrade_state_str);
+
+const char *sl_link_an_lp_caps_state_str(u32 lp_caps_state)
+{
+ switch (lp_caps_state) {
+ case SL_CORE_LINK_LP_CAPS_NOT_RUNNING:
+ return "not_running";
+ case SL_CORE_LINK_LP_CAPS_RUNNING:
+ return "running";
+ case SL_CORE_LINK_LP_CAPS_DATA:
+ return "data";
+ case SL_CORE_LINK_LP_CAPS_TIMEOUT:
+ return "timeout";
+ case SL_CORE_LINK_LP_CAPS_ERROR:
+ return "error";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_link_an_lp_caps_state_str);
+
+const char *sl_link_config_opt_str(u32 option)
+{
+ switch (option) {
+ case SL_LINK_CONFIG_OPT_AUTONEG_ENABLE:
+ return "autoneg-enable";
+ case SL_LINK_CONFIG_OPT_AUTONEG_CONTINUOUS_ENABLE:
+ return "autoneg-continuous";
+ case SL_LINK_CONFIG_OPT_HEADSHELL_LOOPBACK_ENABLE:
+ return "loopback-headshell-enable";
+ case SL_LINK_CONFIG_OPT_REMOTE_LOOPBACK_ENABLE:
+ return "loopback-remote-enable";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_link_config_opt_str);
+
+const char *sl_link_policy_opt_str(u32 option)
+{
+ switch (option) {
+ case SL_LINK_POLICY_OPT_KEEP_SERDES_UP:
+ return "keep-serdes-up";
+ case SL_LINK_POLICY_OPT_USE_UNSUPPORTED_CABLE:
+ return "use-unsupported-cable";
+ case SL_LINK_POLICY_OPT_IGNORE_MEDIA_ERROR:
+ return "ignore-media-error";
+ case SL_LINK_POLICY_OPT_USE_SUPPORTED_SS200_CABLE:
+ return "use-supported-ss200-cable";
+ case SL_LINK_POLICY_OPT_LINK_DOWN_REQ:
+ return "link-down-request";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_link_policy_opt_str);
+
+#define SL_LINK_DOWN_INFO_MASK ( \
+ SL_LINK_DOWN_RETRYABLE |\
+ SL_LINK_DOWN_ORIGIN_ASYNC |\
+ SL_LINK_DOWN_ORIGIN_LINK_UP)
+
+static int sl_link_down_cause_map_masked_str(unsigned long cause_map, char *cause_str, unsigned int cause_str_size,
+ u64 mask)
+{
+ int rtn;
+ int str_pos;
+ int which;
+
+ if (!cause_str)
+ return -EINVAL;
+
+ if (cause_str_size < SL_LINK_DOWN_CAUSE_STR_SIZE_MIN)
+ return -EINVAL;
+
+ cause_map &= mask;
+
+ if (cause_map == SL_LINK_DOWN_CAUSE_NONE) {
+ scnprintf(cause_str, cause_str_size, "none");
+ return 0;
+ }
+
+ str_pos = 0;
+
+ for_each_set_bit(which, &cause_map, sizeof(cause_map) * BITS_PER_BYTE) {
+
+ switch (BIT(which)) {
+ case SL_LINK_DOWN_CAUSE_UCW:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "ucw ");
+ break;
+ case SL_LINK_DOWN_CAUSE_LF:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "lcl-fault ");
+ break;
+ case SL_LINK_DOWN_CAUSE_RF:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "rmt-fault ");
+ break;
+ case SL_LINK_DOWN_CAUSE_DOWN:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "link-down ");
+ break;
+ case SL_LINK_DOWN_CAUSE_UP_TRIES:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "up-tries ");
+ break;
+ case SL_LINK_DOWN_CAUSE_AUTONEG_NOMATCH:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "autoneg-nomatch ");
+ break;
+ case SL_LINK_DOWN_CAUSE_AUTONEG:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "autoneg-fail ");
+ break;
+ case SL_LINK_DOWN_CAUSE_CONFIG:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "config ");
+ break;
+ case SL_LINK_DOWN_CAUSE_INTR_ENABLE:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "intr-enable ");
+ break;
+ case SL_LINK_DOWN_CAUSE_TIMEOUT:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "timeout ");
+ break;
+ case SL_LINK_DOWN_CAUSE_CANCELED:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "canceled ");
+ break;
+ case SL_LINK_DOWN_CAUSE_COMMAND:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "command ");
+ break;
+ case SL_LINK_DOWN_CAUSE_DOWNSHIFT:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "downshift-fail ");
+ break;
+ case SL_LINK_DOWN_CAUSE_LLR_REPLAY_MAX:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "llr-replay-at-max ");
+ break;
+ case SL_LINK_DOWN_CAUSE_UPSHIFT:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "upshift-fail ");
+ break;
+ case SL_LINK_DOWN_CAUSE_AUTONEG_CONFIG:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "an-config ");
+ break;
+ case SL_LINK_DOWN_CAUSE_PCS_FAULT:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "pcs-fault ");
+ break;
+ case SL_LINK_DOWN_CAUSE_SERDES_PLL:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "serdes-pll ");
+ break;
+ case SL_LINK_DOWN_CAUSE_SERDES_CONFIG:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "serdes-config ");
+ break;
+ case SL_LINK_DOWN_CAUSE_SERDES_SIGNAL:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "serdes-signal ");
+ break;
+ case SL_LINK_DOWN_CAUSE_SERDES_QUALITY:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "serdes-quality ");
+ break;
+ case SL_LINK_DOWN_CAUSE_NO_MEDIA:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "no-media ");
+ break;
+ case SL_LINK_DOWN_CAUSE_CCW:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "ccw ");
+ break;
+ case SL_LINK_DOWN_CAUSE_MEDIA_HOT:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "media-hot ");
+ break;
+ case SL_LINK_DOWN_CAUSE_MEDIA_WARM:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "media-warm ");
+ break;
+ case SL_LINK_DOWN_CAUSE_INTR_REGISTER:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "interrupt-reg ");
+ break;
+ case SL_LINK_DOWN_CAUSE_MEDIA_ERROR:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "media-error ");
+ break;
+ case SL_LINK_DOWN_CAUSE_UP_CANCELED:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "up-canceled ");
+ break;
+ case SL_LINK_DOWN_CAUSE_UNSUPPORTED_SPEED:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "unsupported-speed ");
+ break;
+ case SL_LINK_DOWN_CAUSE_SS200_CABLE:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "ss200-cable ");
+ break;
+ case SL_LINK_DOWN_CAUSE_TX_LOL:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "tx-lol ");
+ break;
+ case SL_LINK_DOWN_CAUSE_RX_LOL:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "rx-lol ");
+ break;
+ case SL_LINK_DOWN_CAUSE_TX_LOS:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "tx-los ");
+ break;
+ case SL_LINK_DOWN_CAUSE_RX_LOS:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "rx-los ");
+ break;
+
+ case SL_LINK_DOWN_RETRYABLE:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "retryable ");
+ break;
+ case SL_LINK_DOWN_ORIGIN_ASYNC:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "origin-async ");
+ break;
+ case SL_LINK_DOWN_ORIGIN_LINK_UP:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "origin-up ");
+ break;
+
+ default:
+ rtn = snprintf(cause_str + str_pos, cause_str_size - str_pos, "unknown ");
+ break;
+ }
+
+ if (rtn < 0) {
+ scnprintf(cause_str, cause_str_size, "error");
+ return 0;
+ }
+ if (str_pos + rtn >= cause_str_size) {
+ str_pos = cause_str_size - 1;
+ cause_str[str_pos - 3] = '.';
+ cause_str[str_pos - 2] = '.';
+ cause_str[str_pos - 1] = '.';
+ cause_str[str_pos] = '\0';
+ return 0;
+ }
+ str_pos += rtn;
+ }
+
+ cause_str[str_pos - 1] = '\0';
+
+ return 0;
+}
+
+int sl_link_down_cause_map_str(u64 cause_map, char *cause_str, unsigned int cause_str_size)
+{
+ return sl_link_down_cause_map_masked_str(cause_map, cause_str, cause_str_size, ~SL_LINK_DOWN_INFO_MASK);
+}
+EXPORT_SYMBOL(sl_link_down_cause_map_str);
+
+int sl_link_down_cause_map_with_info_str(u64 cause_map, char *cause_str, unsigned int cause_str_size)
+{
+ return sl_link_down_cause_map_masked_str(cause_map, cause_str, cause_str_size, ~0ULL);
+}
+EXPORT_SYMBOL(sl_link_down_cause_map_with_info_str);
+
+int sl_link_info_map_str(u64 info_map, char *info_map_str, unsigned int info_map_str_size)
+{
+ return sl_core_info_map_str(info_map, info_map_str, info_map_str_size);
+}
+EXPORT_SYMBOL(sl_link_info_map_str);
+
+const char *sl_link_config_pause_str(u32 config)
+{
+ if (config == SL_LINK_CONFIG_PAUSE_ASYM)
+ return "pause-asym";
+ if (config == SL_LINK_CONFIG_PAUSE_SYM)
+ return "pause-sym";
+
+ return "unknown";
+}
+EXPORT_SYMBOL(sl_link_config_pause_str);
+
+const char *sl_link_config_hpe_str(u32 config)
+{
+ if (config == SL_LINK_CONFIG_HPE_LINKTRAIN)
+ return "link-train";
+ if (config == SL_LINK_CONFIG_HPE_PRECODING)
+ return "precode";
+ if (config == SL_LINK_CONFIG_HPE_PCAL)
+ return "progressive-cal";
+ if (config == SL_LINK_CONFIG_HPE_R3)
+ return "R3";
+ if (config == SL_LINK_CONFIG_HPE_R2)
+ return "R2";
+ if (config == SL_LINK_CONFIG_HPE_R1)
+ return "R1";
+ if (config == SL_LINK_CONFIG_HPE_C3)
+ return "C3";
+ if (config == SL_LINK_CONFIG_HPE_C2)
+ return "C2";
+ if (config == SL_LINK_CONFIG_HPE_C1)
+ return "C1";
+ if (config == SL_LINK_CONFIG_HPE_LLR)
+ return "llr";
+
+ return "unknown";
+}
+EXPORT_SYMBOL(sl_link_config_hpe_str);
diff --git a/drivers/net/ethernet/hpe/sl/sl_llr.c b/drivers/net/ethernet/hpe/sl/sl_llr.c
new file mode 100644
index 000000000000..539574d4e428
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_llr.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <linux/hpe/sl/sl_llr.h>
+
+#include "sl_asic.h"
+#include "sl_log.h"
+#include "sl_lgrp.h"
+#include "sl_llr.h"
+#include "sl_ctrl_llr.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_LLR_LOG_NAME
+
+static struct sl_llr llrs[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS];
+
+void sl_llr_init(void)
+{
+ u8 ldev_num;
+ u8 lgrp_num;
+ u8 link_num;
+
+ for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) {
+ for (lgrp_num = 0; lgrp_num < SL_ASIC_MAX_LGRPS; ++lgrp_num) {
+ for (link_num = 0; link_num < SL_ASIC_MAX_LINKS; ++link_num) {
+ llrs[ldev_num][lgrp_num][link_num].magic = SL_LLR_MAGIC;
+ llrs[ldev_num][lgrp_num][link_num].ver = SL_LLR_VER;
+ llrs[ldev_num][lgrp_num][link_num].num = link_num;
+ llrs[ldev_num][lgrp_num][link_num].lgrp_num = lgrp_num;
+ llrs[ldev_num][lgrp_num][link_num].ldev_num = ldev_num;
+ }
+ }
+ }
+}
+
+static int sl_llr_check(struct sl_llr *llr)
+{
+ if (!llr) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL llr");
+ return -EINVAL;
+ }
+ if (IS_ERR(llr)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "llr pointer error");
+ return -EINVAL;
+ }
+ if (llr->magic != SL_LLR_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad llr magic");
+ return -EINVAL;
+ }
+ if (llr->ver != SL_LLR_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "wrong llr version");
+ return -EINVAL;
+ }
+ if (llr->num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad llr num");
+ return -EINVAL;
+ }
+ if (llr->lgrp_num >= SL_ASIC_MAX_LGRPS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp num");
+ return -EINVAL;
+ }
+ if (llr->ldev_num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev num");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_llr_config_check(struct sl_llr_config *llr_config)
+{
+ if (!llr_config) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL llr_config");
+ return -EINVAL;
+ }
+ if (IS_ERR(llr_config)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "llr_config pointer error");
+ return -EINVAL;
+ }
+ if (llr_config->magic != SL_LLR_CONFIG_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad llr_config magic");
+ return -EINVAL;
+ }
+ if (llr_config->ver != SL_LLR_CONFIG_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "wrong llr_config version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sl_llr_policy_check(struct sl_llr_policy *llr_policy)
+{
+ if (!llr_policy) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL llr_policy");
+ return -EINVAL;
+ }
+ if (IS_ERR(llr_policy)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "llr_policy pointer error");
+ return -EINVAL;
+ }
+ if (llr_policy->magic != SL_LLR_POLICY_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad llr_policy magic");
+ return -EINVAL;
+ }
+ if (llr_policy->ver != SL_LLR_POLICY_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "wrong llr_policy version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct sl_llr *sl_llr_new(struct sl_lgrp *lgrp, u8 llr_num, struct kobject *sysfs_parent)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+ if (llr_num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "invalid (llr_num = %u)", llr_num);
+ return ERR_PTR(-EINVAL);
+ }
+
+ rtn = sl_ctrl_llr_new(lgrp->ldev_num, lgrp->num, llr_num, sysfs_parent);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ return &llrs[lgrp->ldev_num][lgrp->num][llr_num];
+}
+EXPORT_SYMBOL(sl_llr_new);
+
+int sl_llr_del(struct sl_llr *llr)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "del fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_del(llr->ldev_num, llr->lgrp_num, llr->num);
+}
+EXPORT_SYMBOL(sl_llr_del);
+
+int sl_llr_config_set(struct sl_llr *llr, struct sl_llr_config *llr_config)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "config set fail");
+ return rtn;
+ }
+ rtn = sl_llr_config_check(llr_config);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "config set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_config_set(llr->ldev_num, llr->lgrp_num, llr->num, llr_config);
+}
+EXPORT_SYMBOL(sl_llr_config_set);
+
+int sl_llr_policy_set(struct sl_llr *llr, struct sl_llr_policy *llr_policy)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+ rtn = sl_llr_policy_check(llr_policy);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "policy set fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_policy_set(llr->ldev_num, llr->lgrp_num, llr->num, llr_policy);
+}
+EXPORT_SYMBOL(sl_llr_policy_set);
+
+int sl_llr_setup(struct sl_llr *llr)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "setup fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_setup(llr->ldev_num, llr->lgrp_num, llr->num);
+}
+EXPORT_SYMBOL(sl_llr_setup);
+
+int sl_llr_start(struct sl_llr *llr)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "start fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_start(llr->ldev_num, llr->lgrp_num, llr->num);
+}
+EXPORT_SYMBOL(sl_llr_start);
+
+int sl_llr_stop(struct sl_llr *llr)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "stop fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_stop(llr->ldev_num, llr->lgrp_num, llr->num);
+}
+EXPORT_SYMBOL(sl_llr_stop);
+
+int sl_llr_state_get(struct sl_llr *llr, enum sl_llr_state *state)
+{
+ int rtn;
+
+ rtn = sl_llr_check(llr);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "state get fail");
+ return rtn;
+ }
+
+ return sl_ctrl_llr_state_get(llr->ldev_num, llr->lgrp_num, llr->num, state);
+}
+EXPORT_SYMBOL(sl_llr_state_get);
+
+const char *sl_llr_state_str(u32 state)
+{
+ switch (state) {
+ case SL_LLR_STATE_INVALID:
+ return "invalid";
+ case SL_LLR_STATE_OFF:
+ return "off";
+ case SL_LLR_STATE_CONFIGURED:
+ return "configured";
+ case SL_LLR_STATE_SETUP_BUSY:
+ return "setup-busy";
+ case SL_LLR_STATE_SETUP:
+ return "setup";
+ case SL_LLR_STATE_START_BUSY:
+ return "start-busy";
+ case SL_LLR_STATE_RUNNING:
+ return "running";
+ case SL_LLR_STATE_CANCELING:
+ return "canceling";
+ case SL_LLR_STATE_STOP_BUSY:
+ return "stop-busy";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_llr_state_str);
+
+const char *sl_llr_link_dn_behavior_str(u32 behavior)
+{
+ switch (behavior) {
+ case SL_LLR_LINK_DN_BEHAVIOR_DISCARD:
+ return "discard";
+ case SL_LLR_LINK_DN_BEHAVIOR_BLOCK:
+ return "block";
+ case SL_LLR_LINK_DN_BEHAVIOR_BEST_EFFORT:
+ return "best-effort";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_llr_link_dn_behavior_str);
diff --git a/drivers/net/ethernet/hpe/sl/sl_mac.c b/drivers/net/ethernet/hpe/sl/sl_mac.c
new file mode 100644
index 000000000000..a8c6ee46721e
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_mac.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/err.h>
+
+#include <linux/hpe/sl/sl_mac.h>
+
+#include "sl_asic.h"
+#include "sl_log.h"
+#include "sl_lgrp.h"
+#include "sl_mac.h"
+#include "sl_ctrl_mac.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_MAC_LOG_NAME
+
+static struct sl_mac macs[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS];
+
+void sl_mac_init(void)
+{
+ u8 ldev_num;
+ u8 lgrp_num;
+ u8 link_num;
+
+ for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) {
+ for (lgrp_num = 0; lgrp_num < SL_ASIC_MAX_LGRPS; ++lgrp_num) {
+ for (link_num = 0; link_num < SL_ASIC_MAX_LINKS; ++link_num) {
+ macs[ldev_num][lgrp_num][link_num].magic = SL_MAC_MAGIC;
+ macs[ldev_num][lgrp_num][link_num].ver = SL_MAC_VER;
+ macs[ldev_num][lgrp_num][link_num].num = link_num;
+ macs[ldev_num][lgrp_num][link_num].lgrp_num = lgrp_num;
+ macs[ldev_num][lgrp_num][link_num].ldev_num = ldev_num;
+ }
+ }
+ }
+}
+
+static int sl_mac_check(struct sl_mac *mac)
+{
+ if (!mac) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL mac");
+ return -EINVAL;
+ }
+ if (IS_ERR(mac)) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "mac pointer error");
+ return -EINVAL;
+ }
+ if (mac->magic != SL_MAC_MAGIC) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad mac magic");
+ return -EINVAL;
+ }
+ if (mac->ver != SL_MAC_VER) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad mac version");
+ return -EINVAL;
+ }
+ if (mac->num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad mac num");
+ return -EINVAL;
+ }
+ if (mac->lgrp_num >= SL_ASIC_MAX_LGRPS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad lgrp num");
+ return -EINVAL;
+ }
+ if (mac->ldev_num >= SL_ASIC_MAX_LDEVS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "bad ldev num");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct sl_mac *sl_mac_new(struct sl_lgrp *lgrp, u8 mac_num, struct kobject *sysfs_parent)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+ if (mac_num >= SL_ASIC_MAX_LINKS) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "invalid (mac_num = %u)", mac_num);
+ return ERR_PTR(-EINVAL);
+ }
+
+ rtn = sl_ctrl_mac_new(lgrp->ldev_num, lgrp->num, mac_num, sysfs_parent);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "new fail");
+ return ERR_PTR(rtn);
+ }
+
+ return &macs[lgrp->ldev_num][lgrp->num][mac_num];
+}
+EXPORT_SYMBOL(sl_mac_new);
+
+int sl_mac_del(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "del fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_del(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_del);
+
+int sl_mac_tx_start(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "tx start fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_tx_start(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_tx_start);
+
+int sl_mac_tx_stop(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "tx stop fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_tx_stop(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_tx_stop);
+
+int sl_mac_tx_state_get(struct sl_mac *mac, u32 *state)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "tx state get fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_tx_state_get(mac->ldev_num, mac->lgrp_num, mac->num, state);
+}
+EXPORT_SYMBOL(sl_mac_tx_state_get);
+
+int sl_mac_rx_start(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "rx start fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_rx_start(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_rx_start);
+
+int sl_mac_rx_stop(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "rx stop fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_rx_stop(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_rx_stop);
+
+int sl_mac_rx_state_get(struct sl_mac *mac, u32 *state)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "rx state get fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_rx_state_get(mac->ldev_num, mac->lgrp_num, mac->num, state);
+}
+EXPORT_SYMBOL(sl_mac_rx_state_get);
+
+int sl_mac_reset(struct sl_mac *mac)
+{
+ int rtn;
+
+ rtn = sl_mac_check(mac);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "reset fail");
+ return rtn;
+ }
+
+ return sl_ctrl_mac_reset(mac->ldev_num, mac->lgrp_num, mac->num);
+}
+EXPORT_SYMBOL(sl_mac_reset);
+
+const char *sl_mac_state_str(u32 state)
+{
+ switch (state) {
+ case SL_MAC_STATE_OFF:
+ return "off";
+ case SL_MAC_STATE_ON:
+ return "on";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL(sl_mac_state_str);
diff --git a/drivers/net/ethernet/hpe/sl/sl_media.c b/drivers/net/ethernet/hpe/sl/sl_media.c
new file mode 100644
index 000000000000..50f60e36645c
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_media.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2022,2023,2024,2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+
+#include <linux/hpe/sl/sl_media.h>
+
+#include "sl_log.h"
+#include "sl_lgrp.h"
+#include "sl_media_jack.h"
+
+#define LOG_BLOCK SL_LOG_BLOCK
+#define LOG_NAME SL_LOG_MEDIA_LOG_NAME
+
+int sl_media_headshell_temp_get(struct sl_lgrp *lgrp, u8 *temp)
+{
+ int rtn;
+
+ rtn = sl_lgrp_check(lgrp);
+ if (rtn) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "headshell temp get fail");
+ return rtn;
+ }
+ if (!temp) {
+ sl_log_err(NULL, LOG_BLOCK, LOG_NAME, "NULL temp");
+ return -EINVAL;
+ }
+
+ return sl_media_jack_cable_temp_get(lgrp->ldev_num, lgrp->num, temp);
+}
+EXPORT_SYMBOL(sl_media_headshell_temp_get);
+
+const char *sl_media_furcation_str(u32 furcation)
+{
+ switch (furcation) {
+ case SL_MEDIA_FURCATION_X1:
+ return "unfurcated";
+ case SL_MEDIA_FURCATION_X2:
+ return "bifurcated";
+ case SL_MEDIA_FURCATION_X4:
+ return "quadfurcated";
+ case SL_MEDIA_FURCATION_UNKNOWN:
+ return "unknown";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_furcation_str);
+
+const char *sl_media_state_str(u8 state)
+{
+ switch (state) {
+ case SL_MEDIA_JACK_CABLE_REMOVED:
+ return "removed";
+ case SL_MEDIA_JACK_CABLE_INSERTED:
+ return "inserted";
+ case SL_MEDIA_JACK_CABLE_GOING_ONLINE:
+ return "going-online";
+ case SL_MEDIA_JACK_CABLE_ONLINE:
+ return "online";
+ case SL_MEDIA_JACK_CABLE_ERROR:
+ return "error";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_state_str);
+
+const char *sl_media_cable_speed_state_str(u8 cable_speed_state)
+{
+ switch (cable_speed_state) {
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_UPSHIFTED:
+ return "400G";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_DOWNSHIFTED:
+ return "200G";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_NO_CABLE:
+ return "failed-no-cable";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_FAKE_CABLE:
+ return "failed-fake-cable";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_NO_SUPPORT:
+ return "failed-no-support";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_CHECK:
+ return "failed-check";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_UPSHIFT:
+ return "failed-up";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_FAILED_DOWNSHIFT:
+ return "failed-down";
+ case SL_MEDIA_JACK_CABLE_SHIFT_STATE_NOTSHIFTED:
+ return "none";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_cable_speed_state_str);
+
+const char *sl_media_type_str(u32 type)
+{
+ switch (type) {
+ case SL_MEDIA_TYPE_HEADSHELL:
+ return "headshell";
+ case SL_MEDIA_TYPE_BACKPLANE:
+ return "backplane";
+ case SL_MEDIA_TYPE_ELECTRICAL:
+ return "electrical";
+ case SL_MEDIA_TYPE_OPTICAL:
+ return "optical";
+ case SL_MEDIA_TYPE_PASSIVE:
+ return "passive";
+ case SL_MEDIA_TYPE_ACTIVE:
+ return "active";
+ case SL_MEDIA_TYPE_ANALOG:
+ return "analog";
+ case SL_MEDIA_TYPE_DIGITAL:
+ return "digital";
+ case SL_MEDIA_TYPE_LOOPBACK:
+ return "loopback";
+ case SL_MEDIA_TYPE_UNSUPPORTED:
+ return "unsupported";
+ case SL_MEDIA_TYPE_UNKNOWN:
+ return "unknown";
+ case SL_MEDIA_TYPE_AOC:
+ return "AOC";
+ case SL_MEDIA_TYPE_POC:
+ return "POC";
+ case SL_MEDIA_TYPE_PEC:
+ return "PEC";
+ case SL_MEDIA_TYPE_AEC:
+ return "AEC";
+ case SL_MEDIA_TYPE_ACC:
+ return "ACC";
+ case SL_MEDIA_TYPE_BKP:
+ return "BKP";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_type_str);
+
+const char *sl_media_shape_str(u32 shape)
+{
+ switch (shape) {
+ case SL_MEDIA_SHAPE_UNKNOWN:
+ return "unknown";
+ case SL_MEDIA_SHAPE_STRAIGHT:
+ return "straight";
+ case SL_MEDIA_SHAPE_SPLITTER:
+ return "splitter(y)";
+ case SL_MEDIA_SHAPE_BIFURCATED:
+ return "bifurcated(H)";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_shape_str);
+
+const char *sl_media_cable_end_str(u8 cable_end)
+{
+ switch (cable_end) {
+ case SL_MEDIA_CABLE_END_DD:
+ return "qsfp_dd";
+ case SL_MEDIA_CABLE_END_END1:
+ return "qsfp_end1";
+ case SL_MEDIA_CABLE_END_END2:
+ return "qsfp_end2";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_cable_end_str);
+
+const char *sl_media_vendor_str(u32 vendor)
+{
+ switch (vendor) {
+ case SL_MEDIA_VENDOR_UNKNOWN:
+ return "unknown";
+ case SL_MEDIA_VENDOR_TE:
+ return "te";
+ case SL_MEDIA_VENDOR_HISENSE:
+ return "hisense";
+ case SL_MEDIA_VENDOR_LEONI:
+ return "leoni";
+ case SL_MEDIA_VENDOR_FINISAR:
+ return "finisar";
+ case SL_MEDIA_VENDOR_MOLEX:
+ return "molex";
+ case SL_MEDIA_VENDOR_BIZLINK:
+ return "bizlink";
+ case SL_MEDIA_VENDOR_HPE:
+ return "hpe";
+ case SL_MEDIA_VENDOR_CLOUD_LIGHT:
+ return "cloud-light";
+ case SL_MEDIA_VENDOR_MULTILANE:
+ return "multilane";
+ case SL_MEDIA_VENDOR_AMPHENOL:
+ return "amphenol";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_vendor_str);
+
+const char *sl_media_speed_str(u32 speed)
+{
+ switch (speed) {
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_800G:
+ return "ck800G";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_400G:
+ return "ck400G";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_200G:
+ return "ck200G";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_100G:
+ return "ck100G";
+ case SL_MEDIA_SPEEDS_SUPPORT_BS_200G:
+ return "bs200G";
+ case SL_MEDIA_SPEEDS_SUPPORT_BJ_100G:
+ return "bj100G";
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_100G:
+ return "cd100G";
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_50G:
+ return "cd50G";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_speed_str);
+
+const char *sl_media_ber_str(u8 media_interface)
+{
+ switch (media_interface) {
+ case 1:
+ return "<1E-12";
+ case 2:
+ return "<5E-5";
+ case 3:
+ return "<2.6E-4";
+ case 4:
+ return "<1E-6";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_ber_str);
+
+const char *sl_media_host_interface_str(u32 speed, u32 type)
+{
+ if ((type == SL_MEDIA_TYPE_PEC) || (type == SL_MEDIA_TYPE_BKP)) {
+ switch (speed) {
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_50G:
+ return "50GBASE-CR";
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_100G:
+ return "100GBASE-CR2";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_100G:
+ return "100GBASE-CR1";
+ case SL_MEDIA_SPEEDS_SUPPORT_BS_200G:
+ return "200GBASE-CR4";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_200G:
+ return "200GBASE-CR2";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_400G:
+ return "400GBASE-CR4";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_800G:
+ return "800GBASE-CR8";
+ case SL_MEDIA_SPEEDS_SUPPORT_BJ_100G:
+ return "BJ-100G";
+ default:
+ return "invalid";
+ }
+ }
+
+ switch (speed) {
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_50G:
+ return "50GAUI-1-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_CD_100G:
+ return "100GAUI-2-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_100G:
+ return "100GAUI-1-L-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_BS_200G:
+ return "200GAUI-4-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_200G:
+ return "200GAUI-2-S-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_400G:
+ return "400GAUI-4-S-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_CK_800G:
+ return "800GAUI-8-S-C2M";
+ case SL_MEDIA_SPEEDS_SUPPORT_BJ_100G:
+ return "BJ-100G";
+ default:
+ return "invalid";
+ }
+
+}
+EXPORT_SYMBOL(sl_media_host_interface_str);
+
+const char *sl_media_info_str(u32 info)
+{
+ switch (info) {
+ case SL_MEDIA_INFO_AUTONEG:
+ return "autoneg";
+ case SL_MEDIA_INFO_FAKE:
+ return "fake";
+ case SL_MEDIA_INFO_RESETTABLE:
+ return "resettable";
+ case SL_MEDIA_INFO_LINKTRAIN:
+ return "linktrain";
+ case SL_MEDIA_INFO_SUPPORTED_SS200_CABLE:
+ return "supported-ss200-cable";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_info_str);
+
+const char *sl_media_error_str(u32 error)
+{
+ switch (error) {
+ case SL_MEDIA_ERROR_CABLE_UNSUPPORTED:
+ return "unsupported";
+ case SL_MEDIA_ERROR_CABLE_FORMAT_UNSUPPORTED:
+ return "unsupported-format";
+ case SL_MEDIA_ERROR_CABLE_FW_UNSUPPORTED:
+ return "unsupported-fw";
+ case SL_MEDIA_ERROR_CABLE_HEADSHELL_FAULT:
+ return "headshell-fault";
+ case SL_MEDIA_ERROR_TEMP_DOWN_LIMIT_DEFAULT:
+ return "temperature-down-limit-default";
+ case SL_MEDIA_ERROR_TEMP_WARN_LIMIT_DEFAULT:
+ return "temperature-warn-limit-default";
+ case SL_MEDIA_ERROR_TRYABLE:
+ return "tryable";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_error_str);
+
+const char *sl_media_jack_type_str(u32 jack_type, u32 density)
+{
+ switch (jack_type) {
+ case SL_MEDIA_JACK_TYPE_BACKPLANE:
+ return "backplane";
+ case SL_MEDIA_JACK_TYPE_SFP:
+ return "sfp";
+ case SL_MEDIA_JACK_TYPE_QSFP:
+ if (density == SL_MEDIA_QSFP_DENSITY_DOUBLE)
+ return "qsfp-dd";
+ return "qsfp";
+ case SL_MEDIA_JACK_TYPE_OSFP:
+ return "osfp";
+ case SL_MEDIA_JACK_TYPE_UNKNOWN:
+ return "unknown";
+ case SL_MEDIA_JACK_TYPE_UNSUPPORTED:
+ return "unsupported";
+ default:
+ return "invalid";
+ }
+}
+EXPORT_SYMBOL(sl_media_jack_type_str);
diff --git a/drivers/net/ethernet/hpe/sl/sl_media_nic.c b/drivers/net/ethernet/hpe/sl/sl_media_nic.c
new file mode 100644
index 000000000000..23af513e6bd9
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_media_nic.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2025 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <linux/hpe/sl/sl_lgrp.h>
+
+#include "sl_lgrp.h"
+
+int sl_media_cable_insert(struct sl_lgrp *lgrp, u8 *eeprom_page0, u8 *eeprom_page1, u32 flags)
+{
+ /* placeholder for future functionality */
+ return 0;
+}
+EXPORT_SYMBOL(sl_media_cable_insert);
+
+int sl_media_cable_remove(struct sl_lgrp *lgrp)
+{
+ /* placeholder for future functionality */
+ return 0;
+}
+EXPORT_SYMBOL(sl_media_cable_remove);
diff --git a/drivers/net/ethernet/hpe/sl/sl_module.c b/drivers/net/ethernet/hpe/sl/sl_module.c
new file mode 100644
index 000000000000..b63efedf94ff
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sl/sl_module.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2023-2026 Hewlett Packard Enterprise Development LP */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+
+#include "sl_module.h"
+
+#include "sl.h"
+#include "sl_ldev.h"
+#include "sl_lgrp.h"
+#include "sl_link.h"
+#include "sl_llr.h"
+#include "sl_mac.h"
+
+static unsigned long serdes_core_io_trace_lgrp_map;
+module_param(serdes_core_io_trace_lgrp_map, ulong, 0644);
+MODULE_PARM_DESC(serdes_core_io_trace_lgrp_map, "serdes core io trace enable link group map");
+
+u64 sl_serdes_core_io_trace_lgrp_map_get(void)
+{
+ return serdes_core_io_trace_lgrp_map;
+}
+
+#define SL_DEVICE_NAME "sl"
+
+#define tostr(x) #x
+#define version_str(a, b, c) tostr(a.b.c)
+#define SL_VERSION_STR version_str(SL_VERSION_MAJOR, SL_VERSION_MINOR, SL_VERSION_INC)
+#define hash_str(h) tostr(h)
+#define SL_GIT_HASH_STR hash_str(GIT_HASH)
+
+void sl_version_get(int *major, int *minor, int *inc)
+{
+ if (major)
+ *major = SL_VERSION_MAJOR;
+ if (minor)
+ *minor = SL_VERSION_MINOR;
+ if (inc)
+ *inc = SL_VERSION_INC;
+}
+EXPORT_SYMBOL(sl_version_get);
+
+char *sl_version_str_get(void)
+{
+ return SL_VERSION_STR;
+}
+
+char *sl_git_hash_str_get(void)
+{
+ return (strlen(SL_GIT_HASH_STR) == 0) ? "unavailable" : SL_GIT_HASH_STR;
+}
+EXPORT_SYMBOL(sl_git_hash_str_get);
+
+static dev_t sl_dev;
+static struct class *sl_class;
+static struct device *sl_device;
+
+static int __init sl_init(void)
+{
+ int rtn;
+
+ pr_info("%s: init v" SL_VERSION_STR "\n", module_name(THIS_MODULE));
+ pr_info("%s: hash " SL_GIT_HASH_STR "\n", module_name(THIS_MODULE));
+
+ rtn = alloc_chrdev_region(&sl_dev, 0, 1, SL_DEVICE_NAME);
+ if (rtn) {
+ pr_err("alloc_chrdev_region failed [%d]", rtn);
+ return -EIO;
+ }
+
+ sl_class = class_create(SL_DEVICE_NAME);
+
+ if (IS_ERR(sl_class)) {
+ rtn = PTR_ERR(sl_class);
+ pr_err("class_create \"%s\" failed [%d]", SL_DEVICE_NAME, rtn);
+ goto out_region;
+ }
+
+ sl_device = device_create(sl_class, NULL, sl_dev, NULL, SL_DEVICE_NAME);
+ if (IS_ERR(sl_device)) {
+ rtn = PTR_ERR(sl_device);
+ pr_err("device_create failed [%d]", rtn);
+ goto out_class;
+ }
+
+ sl_ldev_init();
+ sl_lgrp_init();
+ sl_link_init();
+ sl_llr_init();
+ sl_mac_init();
+
+ return 0;
+
+out_class:
+ class_destroy(sl_class);
+out_region:
+ unregister_chrdev_region(sl_dev, 1);
+
+ return rtn;
+}
+module_init(sl_init);
+
+struct device *sl_device_get(void)
+{
+ return sl_device;
+}
+
+static void __exit sl_exit(void)
+{
+ pr_info("%s: exit\n", module_name(THIS_MODULE));
+
+ device_destroy(sl_class, sl_dev);
+ class_destroy(sl_class);
+ unregister_chrdev_region(sl_dev, 1);
+
+ sl_ldev_exit();
+}
+module_exit(sl_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Slingshot Platform Team <hpcdev_ss_plat@hpe.com>");
+MODULE_DESCRIPTION("Slingshot Link Module");
+MODULE_VERSION(SL_VERSION_STR);
--
2.56.0
next prev parent reply other threads:[~2026-10-30 21:01 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 20:58 [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE Slingshot NIC drivers Doug Ledford
2026-10-30 20:58 ` [PATCH 01/23] net: hpe: ss1: add the Cassini headers Doug Ledford
2026-10-31 13:58 ` Andrew Lunn
2026-10-31 19:34 ` Doug Ledford
2026-10-30 20:58 ` [PATCH 02/23] net: hpe: sbl: add the Slingshot Base Link headers Doug Ledford
2026-10-30 20:58 ` [PATCH 03/23] net: hpe: sbl: add the core instance and configuration layer Doug Ledford
2026-10-30 20:59 ` [PATCH 04/23] net: hpe: sbl: add the SerDes layer Doug Ledford
2026-10-30 20:59 ` [PATCH 05/23] net: hpe: sbl: add the PML link layer Doug Ledford
2026-10-30 20:59 ` [PATCH 06/23] net: hpe: sbl: add link state management Doug Ledford
2026-10-30 20:59 ` [PATCH 07/23] net: hpe: sbl: add the SBM serial bus layer Doug Ledford
2026-10-30 20:59 ` [PATCH 08/23] net: hpe: sbl: add media, FEC and counter support Doug Ledford
2026-10-30 20:59 ` [PATCH 09/23] net: hpe: sl: add the Slingshot Link headers Doug Ledford
2026-10-30 20:59 ` Doug Ledford [this message]
2026-10-30 20:59 ` [PATCH 11/23] net: hpe: sl: add the core link state machines Doug Ledford
2026-10-30 20:59 ` [PATCH 12/23] net: hpe: sl: add the control layer Doug Ledford
2026-10-30 20:59 ` [PATCH 13/23] net: hpe: sl: add media and cable handling Doug Ledford
2026-10-30 20:59 ` [PATCH 14/23] net: hpe: sl: add the sysfs interface Doug Ledford
2026-10-30 20:59 ` [PATCH 15/23] net: hpe: sl: add the NIC binding and logging helpers Doug Ledford
2026-10-30 20:59 ` [PATCH 16/23] net: hpe: ss1: add the Cassini core device layer Doug Ledford
2026-10-30 20:59 ` [PATCH 17/23] net: hpe: ss1: add the datapath and memory translation layers Doug Ledford
2026-10-30 20:59 ` [PATCH 18/23] net: hpe: ss1: add the link layer glue Doug Ledford
2026-10-30 20:59 ` [PATCH 19/23] net: hpe: ss1: add SR-IOV support Doug Ledford
2026-10-30 20:59 ` [PATCH 20/23] net: hpe: ss1: add the CXI core interfaces Doug Ledford
2026-10-30 20:59 ` [PATCH 21/23] net: hpe: ss1: add resource groups, QoS and services Doug Ledford
2026-10-30 20:59 ` [PATCH 22/23] net: hpe: ss1: add the Ethernet personality Doug Ledford
2026-10-30 20:59 ` [PATCH 23/23] net: hpe: wire up the Slingshot NIC drivers Doug Ledford
2026-10-30 22:11 ` [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE " Jakub Kicinski
2026-10-31 20:17 ` Andrew Lunn
2026-11-02 1:02 ` Doug Ledford
2026-11-02 1:35 ` Andrew Lunn
2026-11-02 3:45 ` Doug Ledford
2026-11-02 14:26 ` Andrew Lunn
2026-11-04 4:21 ` Doug Ledford
2026-11-04 13:39 ` Jason Gunthorpe
2026-11-04 16:03 ` Doug Ledford
2026-11-04 14:48 ` Andrew Lunn
2026-11-04 15:04 ` Jason Gunthorpe
2026-11-04 16:13 ` Doug Ledford
2026-11-02 3:01 ` Guenter Roeck
2026-11-02 17:01 ` Doug Ledford
2026-11-02 17:39 ` Andrew Lunn
2026-11-02 18:12 ` Doug Ledford
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030-cxi-netdev-submission-v1-10-8c2c391cc283@hpe.com \
--to=doug.ledford@hpe.com \
--cc=andrew+netdev@lunn.ch \
--cc=christian.koenig@amd.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=nathan@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nsc@kernel.org \
--cc=pabeni@redhat.com \
--cc=richardcochran@gmail.com \
--cc=sandeep.bhoot@hpe.com \
--cc=slingshot-os-team@groups.ext.hpe.com \
--cc=sumit.semwal@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®