[PATCH 10/23] net: hpe: sl: add the module entry points
flat view
HOTtoday
From: Doug Ledford <hidden>
Date: 2026-10-30 21:01:00
Also in:
linux-kbuild, lkml
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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 <redacted>
Signed-off-by: Sandeep Bhoot <redacted>
Signed-off-by: Doug Ledford <redacted>
---
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