Add the SL control layer, which validates and serialises requests from the NIC driver and sysfs onto the core state machines, and delivers link group notifications back. 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 (239), LONG_LINE_STRING (18), LONG_LINE_COMMENT (2) - CHECK: UNNECESSARY_PARENTHESES (21), ALLOC_SIZEOF_STRUCT (4) Co-developed-by: Sandeep Bhoot Signed-off-by: Sandeep Bhoot Co-developed-by: Mamila Santosh Signed-off-by: Mamila Santosh Co-developed-by: Iraimani P Signed-off-by: Iraimani P Signed-off-by: Doug Ledford --- .../ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp.c | 164 +++ .../hpe/sl/ctrl/data/sl_ctrl_data_lgrp_trace.c | 50 + .../ethernet/hpe/sl/ctrl/data/sl_ctrl_data_link.c | 424 +++++++ .../ethernet/hpe/sl/ctrl/data/sl_ctrl_data_llr.c | 64 + drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_ldev.c | 290 +++++ drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp.c | 351 ++++++ .../net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp_notif.c | 258 ++++ drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link.c | 1230 ++++++++++++++++++++ .../ethernet/hpe/sl/ctrl/sl_ctrl_link_counters.c | 388 ++++++ .../net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec.c | 202 ++++ .../ethernet/hpe/sl/ctrl/sl_ctrl_link_fec_priv.c | 539 +++++++++ .../net/ethernet/hpe/sl/ctrl/sl_ctrl_link_priv.c | 865 ++++++++++++++ drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr.c | 630 ++++++++++ .../ethernet/hpe/sl/ctrl/sl_ctrl_llr_counters.c | 136 +++ drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac.c | 536 +++++++++ .../ethernet/hpe/sl/ctrl/sl_ctrl_mac_counters.c | 56 + .../ethernet/hpe/sl/ctrl/sl_ctrl_media_counters.c | 157 +++ 17 files changed, 6340 insertions(+) diff --git a/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp.c b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp.c new file mode 100644 index 000000000000..279e41770f48 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025-2026 Hewlett Packard Enterprise Development LP */ + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_media_lgrp.h" +#include "sl_core_lgrp.h" + +#include "data/sl_core_data_lgrp.h" +#include "data/sl_media_data_lgrp.h" + +#include "sl_ctrl_data_lgrp.h" + +#define LOG_NAME SL_CTRL_LGRP_LOG_NAME + +int sl_ctrl_data_lgrp_err_trace_enable_set(struct sl_ctrl_lgrp *ctrl_lgrp, bool err_trace_enable) +{ + struct sl_core_lgrp *held_lgrp_1 __free(sl_core_lgrp_put) = NULL; + int rtn; + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "set (err_trace_enable = %s)", err_trace_enable ? "true" : "false"); + + held_lgrp_1 = sl_core_lgrp_get(ctrl_lgrp->ctrl_ldev->num, ctrl_lgrp->num); + if (!held_lgrp_1) + return -ENODEV; + spin_lock(&ctrl_lgrp->config_lock); + rtn = sl_core_data_lgrp_err_trace_enable_set(held_lgrp_1, + err_trace_enable); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "core trace enable set failed [%d]", rtn); + spin_unlock(&ctrl_lgrp->config_lock); + return rtn; + } + + rtn = sl_media_data_lgrp_err_trace_enable_set(sl_media_lgrp_get(ctrl_lgrp->ctrl_ldev->num, ctrl_lgrp->num), + err_trace_enable); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "media trace enable set failed [%d]", rtn); + spin_unlock(&ctrl_lgrp->config_lock); + return rtn; + } + + ctrl_lgrp->err_trace_enable = err_trace_enable; + + spin_unlock(&ctrl_lgrp->config_lock); + + return 0; +} + +int sl_ctrl_data_lgrp_warn_trace_set(struct sl_ctrl_lgrp *ctrl_lgrp, bool err_trace_enable) +{ + struct sl_core_lgrp *held_lgrp_1 __free(sl_core_lgrp_put) = NULL; + int rtn; + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "set (warn_trace_enable = %s)", err_trace_enable ? "true" : "false"); + + held_lgrp_1 = sl_core_lgrp_get(ctrl_lgrp->ctrl_ldev->num, ctrl_lgrp->num); + if (!held_lgrp_1) + return -ENODEV; + spin_lock(&ctrl_lgrp->config_lock); + rtn = sl_core_data_lgrp_warn_trace_enable_set(held_lgrp_1, + err_trace_enable); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "core trace enable set failed [%d]", rtn); + spin_unlock(&ctrl_lgrp->config_lock); + return rtn; + } + + rtn = sl_media_data_lgrp_warn_trace_enable_set(sl_media_lgrp_get(ctrl_lgrp->ctrl_ldev->num, ctrl_lgrp->num), + err_trace_enable); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "media trace enable set failed [%d]", rtn); + spin_unlock(&ctrl_lgrp->config_lock); + return rtn; + } + + ctrl_lgrp->warn_trace_enable = err_trace_enable; + + spin_unlock(&ctrl_lgrp->config_lock); + + return 0; +} + +int sl_ctrl_data_lgrp_serdes_lane_io_trace_set(struct sl_ctrl_lgrp *ctrl_lgrp, bool serdes_lane_io_trace) +{ + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "set (serdes_lane_io_trace = %s)", serdes_lane_io_trace ? "enable" : "disable"); + + spin_lock(&ctrl_lgrp->config_lock); + ctrl_lgrp->serdes_lane_io_trace_enable = serdes_lane_io_trace; + spin_unlock(&ctrl_lgrp->config_lock); + + return 0; +} + +int sl_ctrl_data_lgrp_mfs_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *mfs) +{ + spin_lock(&ctrl_lgrp->config_lock); + *mfs = ctrl_lgrp->config.mfs; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (mfs = %u)", *mfs); + + return 0; +} + +int sl_ctrl_data_lgrp_furcation_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *furcation) +{ + spin_lock(&ctrl_lgrp->config_lock); + *furcation = ctrl_lgrp->config.furcation; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (furcation = %u)", *furcation); + + return 0; +} + +int sl_ctrl_data_lgrp_fec_mode_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *fec_mode) +{ + spin_lock(&ctrl_lgrp->config_lock); + *fec_mode = ctrl_lgrp->config.fec_mode; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (fec_mode = %u)", *fec_mode); + + return 0; +} + +int sl_ctrl_data_lgrp_tech_map_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *tech_map) +{ + spin_lock(&ctrl_lgrp->config_lock); + *tech_map = ctrl_lgrp->config.tech_map; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (tech_map = 0x%X)", *tech_map); + + return 0; +} + +int sl_ctrl_data_lgrp_fec_map_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *fec_map) +{ + spin_lock(&ctrl_lgrp->config_lock); + *fec_map = ctrl_lgrp->config.fec_map; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (fec_map = 0x%X)", *fec_map); + + return 0; +} + +int sl_ctrl_data_lgrp_options_get(struct sl_ctrl_lgrp *ctrl_lgrp, u32 *options) +{ + spin_lock(&ctrl_lgrp->config_lock); + *options = ctrl_lgrp->config.options; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "get (options = 0x%X)", *options); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp_trace.c b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp_trace.c new file mode 100644 index 000000000000..5b6dfe73495e --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_lgrp_trace.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025-2026 Hewlett Packard Enterprise Development LP */ + +#include +#include + +#include "data/sl_ctrl_data_lgrp_trace.h" + +#define LOG_NAME SL_CTRL_LGRP_LOG_NAME + +int sl_ctrl_data_lgrp_is_err_trace_enabled(struct sl_ctrl_lgrp *ctrl_lgrp, bool *err_trace_enable) +{ + spin_lock(&ctrl_lgrp->config_lock); + *err_trace_enable = ctrl_lgrp->err_trace_enable; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "is err trace enabled (err_trace_enable = %s)", + *err_trace_enable ? "true" : "false"); + + return 0; +} + +int sl_ctrl_data_lgrp_is_warn_trace_enabled(struct sl_ctrl_lgrp *ctrl_lgrp, bool *warn_trace_enable) +{ + spin_lock(&ctrl_lgrp->config_lock); + *warn_trace_enable = ctrl_lgrp->warn_trace_enable; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "is warn trace enabled (warn_trace_enable = %s)", + *warn_trace_enable ? "true" : "false"); + + return 0; +} + +bool sl_ctrl_data_lgrp_is_serdes_lane_io_trace_enabled(struct sl_ctrl_lgrp *ctrl_lgrp) +{ + bool serdes_lane_io_trace; + + spin_lock(&ctrl_lgrp->config_lock); + serdes_lane_io_trace = ctrl_lgrp->serdes_lane_io_trace_enable; + spin_unlock(&ctrl_lgrp->config_lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "is serdes lane io trace enabled (serdes_lane_io_trace = %s)", + serdes_lane_io_trace ? "enabled" : "disabled"); + + return serdes_lane_io_trace; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_link.c b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_link.c new file mode 100644 index 000000000000..dfb1b820d5ef --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_link.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025 Hewlett Packard Enterprise Development LP */ + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_link.h" +#include "data/sl_core_data_link.h" + +#include "data/sl_ctrl_data_link.h" + +#define LOG_NAME SL_CTRL_LINK_LOG_NAME + +int sl_ctrl_data_link_state_get(struct sl_ctrl_link *ctrl_link, u32 *link_state) +{ + struct sl_core_link *held_link_1 __free(sl_core_link_put) = NULL; + int rtn; + u32 core_link_state; + u32 ctrl_link_state; + + held_link_1 = sl_core_link_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num); + if (!held_link_1) + return -ENODEV; + spin_lock(&ctrl_link->data_lock); + rtn = sl_core_data_link_state_get(held_link_1, &core_link_state); + ctrl_link_state = ctrl_link->state; + spin_unlock(&ctrl_link->data_lock); + + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "sl_core_data_link_state_get failed (rtn = %d)", rtn); + return rtn; + } + + *link_state = (core_link_state == SL_CORE_LINK_STATE_AN) ? SL_LINK_STATE_AN : ctrl_link_state; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (link_state = %u %s)", + *link_state, + sl_link_state_str(*link_state)); + + return 0; +} + +int sl_ctrl_data_link_fec_up_settle_wait_ms_get(struct sl_ctrl_link *ctrl_link, u32 *fec_up_settle_wait_ms) +{ + spin_lock(&ctrl_link->data_lock); + *fec_up_settle_wait_ms = ctrl_link->config.fec_up_settle_wait_ms; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (fec_up_settle_wait_ms = %d)", *fec_up_settle_wait_ms); + + return 0; +} + +int sl_ctrl_data_link_fec_up_check_wait_ms_get(struct sl_ctrl_link *ctrl_link, s32 *fec_up_check_wait_ms) +{ + spin_lock(&ctrl_link->data_lock); + *fec_up_check_wait_ms = ctrl_link->config.fec_up_check_wait_ms; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (fec_up_check_wait_ms = %d)", *fec_up_check_wait_ms); + + return 0; +} + +int sl_ctrl_data_link_fec_up_ucw_limit_get(struct sl_ctrl_link *ctrl_link, s32 *fec_up_ucw_limit) +{ + spin_lock(&ctrl_link->data_lock); + *fec_up_ucw_limit = ctrl_link->config.fec_up_ucw_limit; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (fec_up_ucw_limit = %d)", *fec_up_ucw_limit); + + return 0; +} + +int sl_ctrl_data_link_fec_up_ccw_limit_get(struct sl_ctrl_link *ctrl_link, s32 *fec_up_ccw_limit) +{ + spin_lock(&ctrl_link->data_lock); + *fec_up_ccw_limit = ctrl_link->config.fec_up_ccw_limit; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (fec_up_ccw_limit = %d)", *fec_up_ccw_limit); + + return 0; +} + +int sl_ctrl_data_link_policy_fec_mon_period_ms_get(struct sl_ctrl_link *ctrl_link, s32 *mon_period_ms) +{ + spin_lock(&ctrl_link->config_lock); + *mon_period_ms = ctrl_link->policy.fec_mon_period_ms; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_period_ms = %d)", *mon_period_ms); + + return 0; +} + +int sl_ctrl_data_link_policy_fec_mon_ucw_down_limit_get(struct sl_ctrl_link *ctrl_link, s32 *mon_ucw_down_limit) +{ + spin_lock(&ctrl_link->config_lock); + *mon_ucw_down_limit = ctrl_link->policy.fec_mon_ucw_down_limit; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_ucw_down_limit = %d)", *mon_ucw_down_limit); + + return 0; +} + +int sl_ctrl_data_link_policy_fec_mon_ucw_warn_limit_get(struct sl_ctrl_link *ctrl_link, s32 *mon_ucw_warn_limit) +{ + spin_lock(&ctrl_link->config_lock); + *mon_ucw_warn_limit = ctrl_link->policy.fec_mon_ucw_warn_limit; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_ucw_warn_limit = %d)", *mon_ucw_warn_limit); + + return 0; +} + +int sl_ctrl_data_link_policy_fec_mon_ccw_down_limit_get(struct sl_ctrl_link *ctrl_link, s32 *mon_ccw_down_limit) +{ + spin_lock(&ctrl_link->config_lock); + *mon_ccw_down_limit = ctrl_link->policy.fec_mon_ccw_down_limit; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_ccw_down_limit = %d)", *mon_ccw_down_limit); + + return 0; +} + +int sl_ctrl_data_link_policy_fec_mon_ccw_warn_limit_get(struct sl_ctrl_link *ctrl_link, s32 *mon_ccw_warn_limit) +{ + spin_lock(&ctrl_link->config_lock); + *mon_ccw_warn_limit = ctrl_link->policy.fec_mon_ccw_warn_limit; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_ccw_warn_limit = %d)", *mon_ccw_warn_limit); + + return 0; +} + +int sl_ctrl_data_link_fec_down_cache_ucw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *ucw) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + *ucw = ctrl_link->fec_down_cache.cw_cntrs.ucw; + spin_unlock(&ctrl_link->fec_down_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (ucw = %llu)", *ucw); + + return 0; +} + +int sl_ctrl_data_link_fec_down_cache_ccw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *ccw) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + *ccw = ctrl_link->fec_down_cache.cw_cntrs.ccw; + spin_unlock(&ctrl_link->fec_down_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (ccw = %llu)", *ccw); + + return 0; +} + +int sl_ctrl_data_link_fec_down_cache_gcw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *gcw) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + *gcw = ctrl_link->fec_down_cache.cw_cntrs.gcw; + spin_unlock(&ctrl_link->fec_down_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (gcw = %llu)", *gcw); + + return 0; +} + +int sl_ctrl_data_link_fec_down_cache_lane_cntr_get(struct sl_ctrl_link *ctrl_link, u8 lane_num, u64 *lane_cntr) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + *lane_cntr = ctrl_link->fec_down_cache.lane_cntrs.lanes[lane_num]; + spin_unlock(&ctrl_link->fec_down_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (lane_num = %d, lane_cntr = %llu)", lane_num, *lane_cntr); + + return 0; +} + +int sl_ctrl_data_link_fec_down_cache_tail_cntr_get(struct sl_ctrl_link *ctrl_link, u8 bin_num, u64 *tail_cntr) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + *tail_cntr = ctrl_link->fec_down_cache.tail_cntrs.ccw_bins[bin_num]; + spin_unlock(&ctrl_link->fec_down_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (bin_num = %d, tail_cntr = %llu)", bin_num, *tail_cntr); + + return 0; +} + +int sl_ctrl_data_link_fec_mon_period_ms_get(struct sl_ctrl_link *ctrl_link, u32 *mon_period_get) + +{ + spin_lock(&ctrl_link->fec_data.lock); + *mon_period_get = ctrl_link->fec_data.info.monitor.period_ms; + spin_unlock(&ctrl_link->fec_data.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (mon_period_get = %u)", *mon_period_get); + + return 0; +} + +int sl_ctrl_data_link_fec_mon_ucw_down_limit_get(struct sl_ctrl_link *ctrl_link, s32 *ucw_down_limit) +{ + u32 period_ms; + + spin_lock(&ctrl_link->fec_data.lock); + period_ms = ctrl_link->fec_data.info.monitor.period_ms; + *ucw_down_limit = ctrl_link->fec_data.info.monitor.ucw_down_limit; + spin_unlock(&ctrl_link->fec_data.lock); + + if (!period_ms) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fec_mon_ucw_down_limit_get failed (monitoring disabled)"); + return -EBADRQC; + } + + return 0; +} + +int sl_ctrl_data_link_fec_mon_ccw_down_limit_get(struct sl_ctrl_link *ctrl_link, s32 *ccw_down_limit) +{ + u32 period_ms; + + spin_lock(&ctrl_link->fec_data.lock); + period_ms = ctrl_link->fec_data.info.monitor.period_ms; + *ccw_down_limit = ctrl_link->fec_data.info.monitor.ccw_down_limit; + spin_unlock(&ctrl_link->fec_data.lock); + + if (!period_ms) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fec_mon_ccw_down_limit_get failed (monitoring disabled)"); + return -EBADRQC; + } + + return 0; +} + +int sl_ctrl_data_link_fec_mon_ccw_warn_limit_get(struct sl_ctrl_link *ctrl_link, s32 *ccw_warn_limit) +{ + u32 period_ms; + + spin_lock(&ctrl_link->fec_data.lock); + period_ms = ctrl_link->fec_data.info.monitor.period_ms; + *ccw_warn_limit = ctrl_link->fec_data.info.monitor.ccw_warn_limit; + spin_unlock(&ctrl_link->fec_data.lock); + + if (!period_ms) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fec_mon_ccw_warn_limit_get failed (monitoring disabled)"); + return -EBADRQC; + } + + return 0; +} + +int sl_ctrl_data_link_fec_mon_ucw_warn_limit_get(struct sl_ctrl_link *ctrl_link, s32 *ucw_warn_limit) +{ + u32 period_ms; + + spin_lock(&ctrl_link->fec_data.lock); + period_ms = ctrl_link->fec_data.info.monitor.period_ms; + *ucw_warn_limit = ctrl_link->fec_data.info.monitor.ucw_warn_limit; + spin_unlock(&ctrl_link->fec_data.lock); + + if (!period_ms) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fec_mon_ucw_warn_limit_get failed (monitoring disabled)"); + return -EBADRQC; + } + + return 0; +} + +int sl_ctrl_data_link_fec_up_cache_ucw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *ucw) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + *ucw = ctrl_link->fec_up_cache.cw_cntrs.ucw; + spin_unlock(&ctrl_link->fec_up_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (ucw = %llu)", *ucw); + + return 0; +} + +int sl_ctrl_data_link_fec_up_cache_ccw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *ccw) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + *ccw = ctrl_link->fec_up_cache.cw_cntrs.ccw; + spin_unlock(&ctrl_link->fec_up_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (ccw = %llu)", *ccw); + + return 0; +} + +int sl_ctrl_data_link_fec_up_cache_gcw_cntr_get(struct sl_ctrl_link *ctrl_link, u64 *gcw) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + *gcw = ctrl_link->fec_up_cache.cw_cntrs.gcw; + spin_unlock(&ctrl_link->fec_up_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (gcw = %llu)", *gcw); + + return 0; +} + +int sl_ctrl_data_link_fec_up_cache_lane_cntr_get(struct sl_ctrl_link *ctrl_link, u8 lane_num, u64 *lane_cntr) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + *lane_cntr = ctrl_link->fec_up_cache.lane_cntrs.lanes[lane_num]; + spin_unlock(&ctrl_link->fec_up_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (lane_num = %d, lane_cntr = %llu)", lane_num, *lane_cntr); + + return 0; +} + +int sl_ctrl_data_link_fec_up_cache_tail_cntr_get(struct sl_ctrl_link *ctrl_link, u8 bin_num, u64 *tail_cntr) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + *tail_cntr = ctrl_link->fec_up_cache.tail_cntrs.ccw_bins[bin_num]; + spin_unlock(&ctrl_link->fec_up_cache.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (bin_num = %d, tail_cntr = %llu)", bin_num, *tail_cntr); + + return 0; +} + +int sl_ctrl_data_link_policy_options_get(struct sl_ctrl_link *ctrl_link, u32 *options) +{ + spin_lock(&ctrl_link->config_lock); + *options = ctrl_link->policy.options; + spin_unlock(&ctrl_link->config_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (options = 0x%08x)", *options); + + return 0; +} + +int sl_ctrl_data_link_up_timeout_ms_get(struct sl_ctrl_link *ctrl_link, u32 *link_up_timeout_ms) +{ + spin_lock(&ctrl_link->data_lock); + *link_up_timeout_ms = ctrl_link->config.link_up_timeout_ms; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (link_up_timeout_ms = %d)", *link_up_timeout_ms); + + return 0; +} + +int sl_ctrl_data_link_up_tries_max_get(struct sl_ctrl_link *ctrl_link, u32 *link_up_tries_max) +{ + spin_lock(&ctrl_link->data_lock); + *link_up_tries_max = ctrl_link->config.link_up_tries_max; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (link_up_tries_max = %d)", *link_up_tries_max); + + return 0; +} + +int sl_ctrl_data_link_config_options_get(struct sl_ctrl_link *ctrl_link, u32 *options) +{ + spin_lock(&ctrl_link->data_lock); + *options = ctrl_link->config.options; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (options = 0x%08x)", *options); + + return 0; +} + +int sl_ctrl_data_link_pause_map_get(struct sl_ctrl_link *ctrl_link, u32 *pause_map) +{ + spin_lock(&ctrl_link->data_lock); + *pause_map = ctrl_link->config.pause_map; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (pause_map = 0x%x)", *pause_map); + + return 0; +} + +int sl_ctrl_data_link_hpe_map_get(struct sl_ctrl_link *ctrl_link, u32 *hpe_map) +{ + spin_lock(&ctrl_link->data_lock); + *hpe_map = ctrl_link->config.hpe_map; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "get (hpe_map = 0x%x)", *hpe_map); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_llr.c b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_llr.c new file mode 100644 index 000000000000..be742b146a0a --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/data/sl_ctrl_data_llr.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025 Hewlett Packard Enterprise Development LP */ + +#include + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_data_llr.h" + +#define LOG_NAME SL_CTRL_LLR_LOG_NAME + +int sl_ctrl_data_llr_config_mode_get(struct sl_ctrl_llr *ctrl_llr, u32 *mode) +{ + spin_lock(&ctrl_llr->data_lock); + *mode = ctrl_llr->config.mode; + spin_unlock(&ctrl_llr->data_lock); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (mode = %u)\n", *mode); + + return 0; +} + +int sl_ctrl_data_llr_config_setup_timeout_ms_get(struct sl_ctrl_llr *ctrl_llr, u32 *setup_timeout_ms) +{ + spin_lock(&ctrl_llr->data_lock); + *setup_timeout_ms = ctrl_llr->config.setup_timeout_ms; + spin_unlock(&ctrl_llr->data_lock); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (setup_timeout_ms = %u)\n", *setup_timeout_ms); + + return 0; +} + +int sl_ctrl_data_llr_config_start_timeout_ms_get(struct sl_ctrl_llr *ctrl_llr, u32 *start_timeout_ms) +{ + spin_lock(&ctrl_llr->data_lock); + *start_timeout_ms = ctrl_llr->config.start_timeout_ms; + spin_unlock(&ctrl_llr->data_lock); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (start_timeout_ms = %u)\n", *start_timeout_ms); + + return 0; +} + +int sl_ctrl_data_llr_config_link_dn_behavior_get(struct sl_ctrl_llr *ctrl_llr, u32 *link_dn_behavior) +{ + spin_lock(&ctrl_llr->data_lock); + *link_dn_behavior = ctrl_llr->config.link_dn_behavior; + spin_unlock(&ctrl_llr->data_lock); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (link_dn_behavior = %u)\n", *link_dn_behavior); + + return 0; +} + +int sl_ctrl_data_llr_config_options_get(struct sl_ctrl_llr *ctrl_llr, u32 *options) +{ + spin_lock(&ctrl_llr->data_lock); + *options = ctrl_llr->config.options; + spin_unlock(&ctrl_llr->data_lock); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (options = 0x%08x)\n", *options); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_ldev.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_ldev.c new file mode 100644 index 000000000000..0295dd902d6d --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_ldev.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2021-2023,2024,2025 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include +#include + +#include "sl_asic.h" +#include "sl_sysfs.h" +#include "base/sl_ctrl_log.h" +#include "sl_core_ldev.h" +#include "sl_media_ldev.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" + +#define LOG_NAME SL_CTRL_LDEV_LOG_NAME + +#define SL_CTRL_LDEV_DEL_TIMEOUT_MS 2000 + +static struct sl_ctrl_ldev *ctrl_ldevs[SL_ASIC_MAX_LDEVS]; +static DEFINE_SPINLOCK(ctrl_ldevs_lock); + +#define CONFIG_SL_DEFAULT_WQ_MAX_ACTIVE 0 /* 0 is default (256 work items in queue). */ +int sl_ctrl_ldev_new(u8 ldev_num, struct workqueue_struct *workq, + struct sl_ldev_attr *ldev_attr) +{ + int rtn; + struct sl_ctrl_ldev *ctrl_ldev; + + ctrl_ldev = sl_ctrl_ldev_get(ldev_num); + if (ctrl_ldev) { + sl_ctrl_log_err(NULL, LOG_NAME, "exists (ldev = 0x%p)", ctrl_ldev); + return -EBADRQC; + } + + ctrl_ldev = kzalloc(sizeof(struct sl_ctrl_ldev), GFP_KERNEL); + if (!ctrl_ldev) + return -ENOMEM; + + ctrl_ldev->magic = SL_CTRL_LDEV_MAGIC; + ctrl_ldev->ver = SL_CTRL_LDEV_VER; + ctrl_ldev->num = ldev_num; + ctrl_ldev->attr = *ldev_attr; + + kref_init(&ctrl_ldev->ref_cnt); + init_completion(&ctrl_ldev->del_complete); + + spin_lock_init(&ctrl_ldev->data_lock); + + if (IS_ERR_OR_NULL(workq)) { + ctrl_ldev->workq = alloc_workqueue("%s%u", + WQ_MEM_RECLAIM | WQ_PERCPU, + CONFIG_SL_DEFAULT_WQ_MAX_ACTIVE, "sl-ldev", ldev_num); + if (!ctrl_ldev->workq) { + rtn = -ENOMEM; + sl_ctrl_log_err(ctrl_ldev, LOG_NAME, "alloc_workqueue failed [%d]", rtn); + goto out; + } + + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "created workqueue (workq = 0x%p)", ctrl_ldev->workq); + ctrl_ldev->create_workq = true; + } else { + ctrl_ldev->workq = workq; + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "client workqueue (workq = 0x%p)", ctrl_ldev->workq); + } + + ctrl_ldev->notif_workq = alloc_workqueue("%s%u-notif", + WQ_MEM_RECLAIM | WQ_PERCPU, + CONFIG_SL_DEFAULT_WQ_MAX_ACTIVE, "sl-ldev", ldev_num); + if (!ctrl_ldev->notif_workq) { + rtn = -ENOMEM; + sl_ctrl_log_err(ctrl_ldev, LOG_NAME, "alloc_workqueue notif failed [%d]", rtn); + goto out_del_wq; + } + + rtn = sl_core_ldev_new(ldev_num, ctrl_ldev->attr.accessors, + ctrl_ldev->attr.ops, ctrl_ldev->workq); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_ldev, LOG_NAME, "core_ldev_new failed [%d]", rtn); + goto out_del_notif_wq; + } + + rtn = sl_media_ldev_new(ldev_num, ctrl_ldev->workq); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_ldev, LOG_NAME, "media_ldev_new failed [%d]", rtn); + goto out_del_core; + } + + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "new (ldev = 0x%p)", ctrl_ldev); + + spin_lock(&ctrl_ldevs_lock); + ctrl_ldevs[ldev_num] = ctrl_ldev; + spin_unlock(&ctrl_ldevs_lock); + + return 0; + +out_del_core: + sl_core_ldev_del(ldev_num); +out_del_notif_wq: + destroy_workqueue(ctrl_ldev->notif_workq); + +out_del_wq: + if (ctrl_ldev->create_workq) + destroy_workqueue(ctrl_ldev->workq); +out: + kfree(ctrl_ldev); + + return rtn; +} + +#define SERDES_INIT_MAX_TRIES 3 +#define SERDES_INIT_DELAY_MS 1000 +int sl_ctrl_ldev_serdes_init(u8 ldev_num) +{ + int rtn; + int tries; + struct sl_ctrl_ldev *ctrl_ldev; + + ctrl_ldev = sl_ctrl_ldev_get(ldev_num); + if (!ctrl_ldev) { + sl_ctrl_log_err(NULL, LOG_NAME, "ldev not found (ldev_num = %u)", ldev_num); + return -EBADRQC; + } + + if (!sl_ctrl_ldev_kref_get_unless_zero(ctrl_ldev)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (ldev = 0x%p)", ctrl_ldev); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "serdes init (ldev = 0x%p)", ctrl_ldev); + + tries = SERDES_INIT_MAX_TRIES; + do { + rtn = sl_core_ldev_serdes_init(ldev_num); + if (rtn == 0) + goto out; + tries--; + if (tries) + msleep(SERDES_INIT_DELAY_MS); + } while (tries); + +out: + if (sl_ctrl_ldev_put(ctrl_ldev)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "serdes init - ldev removed (ldev = 0x%p)", ctrl_ldev); + + return rtn; +} + +static void sl_ctrl_ldev_release(struct kref *ref) +{ + struct sl_ctrl_ldev *ctrl_ldev; + u8 ldev_num; + u8 lgrp_num; + int rtn; + + ctrl_ldev = container_of(ref, struct sl_ctrl_ldev, ref_cnt); + ldev_num = ctrl_ldev->num; + + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "release (ldev = 0x%p)", ctrl_ldev); + + /* Not repeated on a retry: the sysfs delete drops kobject references. */ + if (!ctrl_ldev->children_deleted) { + for (lgrp_num = 0; lgrp_num < SL_ASIC_MAX_LGRPS; ++lgrp_num) { + rtn = sl_ctrl_lgrp_del(ldev_num, lgrp_num); + if (rtn && rtn != -EBADRQC) + goto retry; + } + ctrl_ldev->children_deleted = true; + } + + if (!ctrl_ldev->sysfs_deleted) { + /* Must delete sysfs first to guarantee nobody is reading */ + sl_sysfs_ldev_delete(ctrl_ldev); + ctrl_ldev->sysfs_deleted = true; + } + + rtn = sl_core_ldev_del(ldev_num); + if (rtn) { + /* + * A core lgrp or link refused deletion, so the core ldev is + * still published and uses the workqueue and ldev state. + * Nobody holds a reference any more, so re-arm one for the + * next sl_ctrl_ldev_del() to retry, and wake the waiter so it + * can report the failure. + */ + sl_ctrl_log_err_trace(ctrl_ldev, LOG_NAME, + "release - core_ldev_del failed, ldev kept [%d]", rtn); + goto retry; + } + sl_media_ldev_del(ldev_num); + + destroy_workqueue(ctrl_ldev->notif_workq); + + if (ctrl_ldev->create_workq) + destroy_workqueue(ctrl_ldev->workq); + + spin_lock(&ctrl_ldevs_lock); + ctrl_ldevs[ldev_num] = NULL; + spin_unlock(&ctrl_ldevs_lock); + + complete_all(&ctrl_ldev->del_complete); + + kfree(ctrl_ldev); + return; + +retry: + kref_init(&ctrl_ldev->ref_cnt); + complete_all(&ctrl_ldev->del_complete); +} + +int sl_ctrl_ldev_put(struct sl_ctrl_ldev *ctrl_ldev) +{ + return kref_put(&ctrl_ldev->ref_cnt, sl_ctrl_ldev_release); +} + +int sl_ctrl_ldev_del(u8 ldev_num) +{ + struct sl_ctrl_ldev *ctrl_ldev; + unsigned long timeleft; + + ctrl_ldev = sl_ctrl_ldev_get(ldev_num); + if (!ctrl_ldev) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "del not found (ldev_num = %u)", ldev_num); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_ldev, LOG_NAME, "del (ldev = 0x%p)", ctrl_ldev); + + /* + * Release cannot run before the put below, so rearming a completion + * left over from an earlier failed del is safe. + */ + reinit_completion(&ctrl_ldev->del_complete); + + if (!sl_ctrl_ldev_put(ctrl_ldev)) { + timeleft = wait_for_completion_timeout(&ctrl_ldev->del_complete, + msecs_to_jiffies(SL_CTRL_LDEV_DEL_TIMEOUT_MS)); + + sl_ctrl_log_dbg(NULL, LOG_NAME, + "del completion_timeout (timeleft = %lums)", timeleft); + + if (timeleft == 0) { + sl_ctrl_log_err(NULL, LOG_NAME, + "del timed out (ctrl_ldev = 0x%p)", ctrl_ldev); + return -ETIMEDOUT; + } + } + + /* + * A successful release unpublishes and frees the ldev. If it is still + * published, the core ldev refused deletion and was kept for a retry. + * Only the pointer value is compared, the ldev is not dereferenced. + */ + if (sl_ctrl_ldev_get(ldev_num) == ctrl_ldev) + return -EBUSY; + + return 0; +} + +bool sl_ctrl_ldev_kref_get_unless_zero(struct sl_ctrl_ldev *ctrl_ldev) +{ + return (kref_get_unless_zero(&ctrl_ldev->ref_cnt) != 0); +} + +struct sl_ctrl_ldev *sl_ctrl_ldev_get(u8 ldev_num) +{ + struct sl_ctrl_ldev *ctrl_ldev; + + spin_lock(&ctrl_ldevs_lock); + ctrl_ldev = ctrl_ldevs[ldev_num]; + spin_unlock(&ctrl_ldevs_lock); + + sl_ctrl_log_dbg(NULL, LOG_NAME, "get (ldev_num = %u, ldev = 0x%p)", + ldev_num, ctrl_ldev); + + return ctrl_ldev; +} + +void sl_ctrl_ldev_exit(void) +{ + u8 ldev_num; + + for (ldev_num = 0; ldev_num < SL_ASIC_MAX_LDEVS; ++ldev_num) + sl_ctrl_ldev_del(ldev_num); +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp.c new file mode 100644 index 000000000000..1e7240325fcc --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2021-2023,2024,2025 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include + +#include "sl_asic.h" +#include "sl_sysfs.h" +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_lgrp_notif.h" +#include "sl_ctrl_link.h" +#include "sl_ctrl_llr.h" +#include "sl_ctrl_mac.h" +#include "sl_media_lgrp.h" +#include "sl_core_lgrp.h" + +#define LOG_NAME SL_CTRL_LGRP_LOG_NAME + +#define SL_CTRL_LGRP_DEL_TIMEOUT_MS 2000 + +static struct sl_ctrl_lgrp *ctrl_lgrps[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS]; +static DEFINE_SPINLOCK(ctrl_lgrps_lock); + +int sl_ctrl_lgrp_new(u8 ldev_num, u8 lgrp_num, struct kobject *sysfs_parent) +{ + int rtn; + struct sl_ctrl_ldev *ctrl_ldev; + struct sl_ctrl_lgrp *ctrl_lgrp; + + ctrl_ldev = sl_ctrl_ldev_get(ldev_num); + if (!ctrl_ldev) { + sl_ctrl_log_err(NULL, LOG_NAME, "missing ldev (ldev_num = %u)", ldev_num); + return -EBADRQC; + } + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "exists (lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + ctrl_lgrp = kzalloc(sizeof(struct sl_ctrl_lgrp), GFP_KERNEL); + if (!ctrl_lgrp) + return -ENOMEM; + + snprintf(ctrl_lgrp->log_connect_id, + sizeof(ctrl_lgrp->log_connect_id), "ctrl-lgrp%02u", lgrp_num); + + ctrl_lgrp->magic = SL_CTRL_LGRP_MAGIC; + ctrl_lgrp->ver = SL_CTRL_LGRP_VER; + ctrl_lgrp->num = lgrp_num; + ctrl_lgrp->ctrl_ldev = sl_ctrl_ldev_get(ldev_num); + + kref_init(&ctrl_lgrp->ref_cnt); + init_completion(&ctrl_lgrp->del_complete); + + spin_lock_init(&ctrl_lgrp->data_lock); + spin_lock_init(&ctrl_lgrp->config_lock); + spin_lock_init(&ctrl_lgrp->log_lock); + + spin_lock_init(&(ctrl_lgrp->ctrl_notif.lock)); + + rtn = kfifo_alloc(&ctrl_lgrp->ctrl_notif.fifo, + SL_CTRL_LGRP_NOTIF_MSG_COUNT * sizeof(struct sl_lgrp_notif_msg), + GFP_KERNEL); + if (rtn) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "create notif fifo failed"); + rtn = -ENOMEM; + goto out_free; + } + INIT_WORK(&ctrl_lgrp->notif_work, sl_ctrl_lgrp_notif_work); + + rtn = sl_core_lgrp_new(ldev_num, lgrp_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, "core_lgrp_new failed [%d]", rtn); + goto out_kfifo_free; + } + + rtn = sl_media_lgrp_new(ldev_num, lgrp_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, "media_lgrp_new failed [%d]", rtn); + goto out_core_lgrp; + } + + if (sysfs_parent) { + ctrl_lgrp->parent_kobj = sysfs_parent; + + rtn = sl_sysfs_lgrp_create(ctrl_lgrp); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "sysfs_lgrp_create failed"); + goto out_media_lgrp; + } + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "new (lgrp = 0x%p)", ctrl_lgrp); + + spin_lock(&ctrl_lgrps_lock); + ctrl_lgrps[ldev_num][lgrp_num] = ctrl_lgrp; + spin_unlock(&ctrl_lgrps_lock); + + return 0; + +out_media_lgrp: + sl_media_lgrp_del(ldev_num, lgrp_num); +out_core_lgrp: + sl_core_lgrp_del(ldev_num, lgrp_num); +out_kfifo_free: + kfifo_free(&ctrl_lgrp->ctrl_notif.fifo); +out_free: + kfree(ctrl_lgrp); + + return rtn; +} + +static void sl_ctrl_lgrp_release(struct kref *ref) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + u8 ldev_num; + u8 lgrp_num; + u8 link_num; + int rtn; + + ctrl_lgrp = container_of(ref, struct sl_ctrl_lgrp, ref_cnt); + ldev_num = ctrl_lgrp->ctrl_ldev->num; + lgrp_num = ctrl_lgrp->num; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "release lgrp not found (lgrp_num = %u)", lgrp_num); + return; + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "del (lgrp = 0x%p)", ctrl_lgrp); + + if (!ctrl_lgrp->children_deleted) { + for (link_num = 0; link_num < SL_ASIC_MAX_LINKS; ++link_num) { + rtn = sl_ctrl_link_del(ldev_num, lgrp_num, link_num); + if (rtn && rtn != -EBADRQC) + goto retry; + + rtn = sl_ctrl_llr_del(ldev_num, lgrp_num, link_num); + if (rtn && rtn != -EBADRQC) + goto retry; + + rtn = sl_ctrl_mac_del(ldev_num, lgrp_num, link_num); + if (rtn && rtn != -EBADRQC) + goto retry; + } + ctrl_lgrp->children_deleted = true; + } + + if (!ctrl_lgrp->sysfs_deleted) { + /* Must delete sysfs first to guarantee nobody is reading */ + sl_sysfs_lgrp_delete(ctrl_lgrp); + ctrl_lgrp->sysfs_deleted = true; + } + + rtn = sl_core_lgrp_del(ldev_num, lgrp_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "release - core_lgrp_del failed [%d]", rtn); + goto retry; + } + + sl_media_lgrp_del(ldev_num, lgrp_num); + + cancel_work_sync(&ctrl_lgrp->notif_work); + kfifo_free(&ctrl_lgrp->ctrl_notif.fifo); + + spin_lock(&ctrl_lgrps_lock); + ctrl_lgrps[ldev_num][lgrp_num] = NULL; + spin_unlock(&ctrl_lgrps_lock); + + complete_all(&ctrl_lgrp->del_complete); + + kfree(ctrl_lgrp); + return; + +retry: + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, + "release - child/core deletion failed [%d]", rtn); + kref_init(&ctrl_lgrp->ref_cnt); + complete_all(&ctrl_lgrp->del_complete); +} + +int sl_ctrl_lgrp_put(struct sl_ctrl_lgrp *ctrl_lgrp) +{ + return kref_put(&ctrl_lgrp->ref_cnt, sl_ctrl_lgrp_release); +} + +int sl_ctrl_lgrp_del(u8 ldev_num, u8 lgrp_num) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + unsigned long timeleft; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "del not found (ldev_num = %u, lgrp_num = %u)", ldev_num, lgrp_num); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "del (lgrp = 0x%p)", ctrl_lgrp); + reinit_completion(&ctrl_lgrp->del_complete); + + if (!sl_ctrl_lgrp_put(ctrl_lgrp)) { + timeleft = wait_for_completion_timeout(&ctrl_lgrp->del_complete, + msecs_to_jiffies(SL_CTRL_LGRP_DEL_TIMEOUT_MS)); + + sl_ctrl_log_dbg(NULL, LOG_NAME, + "del completion_timeout (timeleft = %lums)", timeleft); + + if (timeleft == 0) { + sl_ctrl_log_err(NULL, LOG_NAME, + "del timed out (ctrl_lgrp = 0x%p)", ctrl_lgrp); + return -ETIMEDOUT; + } + } + if (sl_ctrl_lgrp_get(ldev_num, lgrp_num) == ctrl_lgrp) + return -EBUSY; + + return 0; +} + +bool sl_ctrl_lgrp_kref_get_unless_zero(struct sl_ctrl_lgrp *ctrl_lgrp) +{ + return (kref_get_unless_zero(&ctrl_lgrp->ref_cnt) != 0); +} + +struct sl_ctrl_lgrp *sl_ctrl_lgrp_get(u8 ldev_num, u8 lgrp_num) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + + spin_lock(&ctrl_lgrps_lock); + ctrl_lgrp = ctrl_lgrps[ldev_num][lgrp_num]; + spin_unlock(&ctrl_lgrps_lock); + + return ctrl_lgrp; +} + +int sl_ctrl_lgrp_connect_id_set(u8 ldev_num, u8 lgrp_num, const char *connect_id) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + unsigned long irq_flags; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "NULL lgrp"); + return -EBADRQC; + } + + if (!sl_ctrl_lgrp_kref_get_unless_zero(ctrl_lgrp)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + spin_lock_irqsave(&ctrl_lgrp->log_lock, irq_flags); + strscpy_pad(ctrl_lgrp->log_connect_id, connect_id, + sizeof(ctrl_lgrp->log_connect_id)); + spin_unlock_irqrestore(&ctrl_lgrp->log_lock, irq_flags); + + sl_media_lgrp_connect_id_set(ldev_num, lgrp_num, connect_id); + + sl_core_lgrp_connect_id_set(ldev_num, lgrp_num, connect_id); + + if (sl_ctrl_lgrp_put(ctrl_lgrp)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "connect_id set - lgrp removed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + + return 0; +} + +int sl_ctrl_lgrp_config_set(u8 ldev_num, u8 lgrp_num, struct sl_lgrp_config *lgrp_config) +{ + int rtn; + struct sl_ctrl_lgrp *ctrl_lgrp; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "NULL lgrp"); + return -EBADRQC; + } + + if (!sl_ctrl_lgrp_kref_get_unless_zero(ctrl_lgrp)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "config set:"); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " mfs = %d", lgrp_config->mfs); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " furcation = 0x%X", lgrp_config->furcation); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " fec_mode = 0x%X", lgrp_config->fec_mode); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " tech_map = 0x%X", lgrp_config->tech_map); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " fec_map = 0x%X", lgrp_config->fec_map); + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, " options = 0x%X", lgrp_config->options); + + rtn = sl_core_lgrp_config_set(ldev_num, lgrp_num, lgrp_config); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_lgrp, LOG_NAME, "core_lgrp_config_set failed [%d]", rtn); + goto out; + } + + spin_lock(&(ctrl_lgrp->config_lock)); + ctrl_lgrp->config = *lgrp_config; + spin_unlock(&(ctrl_lgrp->config_lock)); + + rtn = 0; + +out: + if (sl_ctrl_lgrp_put(ctrl_lgrp)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "config set - lgrp removed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + + return rtn; +} + +int sl_ctrl_lgrp_policy_set(u8 ldev_num, u8 lgrp_num, struct sl_lgrp_policy *lgrp_policy) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "NULL lgrp"); + return -EBADRQC; + } + + if (!sl_ctrl_lgrp_kref_get_unless_zero(ctrl_lgrp)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "policy set"); + + spin_lock(&(ctrl_lgrp->config_lock)); + ctrl_lgrp->policy = *lgrp_policy; + spin_unlock(&(ctrl_lgrp->config_lock)); + + if (sl_ctrl_lgrp_put(ctrl_lgrp)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "policy set - lgrp removed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp_notif.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp_notif.c new file mode 100644 index 000000000000..298b8ab0306c --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_lgrp_notif.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2021-2026 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_lgrp_notif.h" +#include "sl_media_lgrp.h" + +#define LOG_NAME SL_CTRL_LGRP_NOTIF_LOG_NAME + +#define SL_CTRL_LGRP_NOTIF_LIST_STATE_IDLE 0 +#define SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING 1 + +static int sl_ctrl_lgrp_notif_list_state_get(struct sl_ctrl_lgrp *ctrl_lgrp) +{ + int notif_list_state; + + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + notif_list_state = ctrl_lgrp->ctrl_notif.list_state; + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + + return notif_list_state; +} + +int sl_ctrl_lgrp_notif_callback_reg(u8 ldev_num, u8 lgrp_num, sl_lgrp_notif_t callback, + u32 types, void *tag) +{ + int rtn; + struct sl_ctrl_lgrp *ctrl_lgrp; + u8 reg_idx; + u8 counter; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "NULL ctrl_lgrp"); + return -EBADRQC; + } + + if (!sl_ctrl_lgrp_kref_get_unless_zero(ctrl_lgrp)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "notif callback reg (types = 0x%X, tag = 0x%p)", + types, tag); + + counter = 0; + while (sl_ctrl_lgrp_notif_list_state_get(ctrl_lgrp) == SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING) { + msleep(20); + counter++; + if (counter >= 10) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "registration timed out"); + rtn = -ETIMEDOUT; + goto out; + } + } + + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + if (ctrl_lgrp->ctrl_notif.list_state == SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING) { + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "notif list is currently being used"); + rtn = -EBUSY; + goto out; + } + for (reg_idx = 0; reg_idx < ARRAY_SIZE(ctrl_lgrp->ctrl_notif.reg_entry); ++reg_idx) { + if (ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].types == 0) { + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].callback = callback; + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].tag = tag; + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].types = types; + break; + } + } + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + + if (reg_idx >= ARRAY_SIZE(ctrl_lgrp->ctrl_notif.reg_entry)) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "registration list is full"); + rtn = -ENOSPC; + goto out; + } + + if (types & SL_LGRP_NOTIF_MEDIA_PRESENT) + sl_media_lgrp_real_cable_if_present_send(ldev_num, lgrp_num); + + if (types & SL_LGRP_NOTIF_MEDIA_NOT_PRESENT) + sl_media_lgrp_real_cable_if_not_present_send(ldev_num, lgrp_num); + + if (types & SL_LGRP_NOTIF_MEDIA_HOT) + sl_media_lgrp_cable_hot_send(ldev_num, lgrp_num); + + if (types & SL_LGRP_NOTIF_MEDIA_WARM) + sl_media_lgrp_cable_warm_send(ldev_num, lgrp_num); + + if (types & SL_LGRP_NOTIF_MEDIA_COLD) + sl_media_lgrp_cable_cold_send(ldev_num, lgrp_num); + + rtn = 0; +out: + if (sl_ctrl_lgrp_put(ctrl_lgrp)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "notif callback reg - lgrp removed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + + return rtn; +} + +int sl_ctrl_lgrp_notif_callback_unreg(u8 ldev_num, u8 lgrp_num, sl_lgrp_notif_t callback, + u32 types) +{ + int rtn; + struct sl_ctrl_lgrp *ctrl_lgrp; + u8 reg_idx; + u8 counter; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "NULL ctrl_lgrp"); + return -EBADRQC; + } + + if (!sl_ctrl_lgrp_kref_get_unless_zero(ctrl_lgrp)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "kref_get_unless_zero failed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + return -EBADRQC; + } + + sl_media_lgrp_cable_temp_flags_clr(ldev_num, lgrp_num); + + counter = 0; + while (sl_ctrl_lgrp_notif_list_state_get(ctrl_lgrp) == SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING) { + msleep(20); + counter++; + if (counter >= 10) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "unregistration timed out"); + rtn = -ETIMEDOUT; + goto out; + } + } + + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + if (ctrl_lgrp->ctrl_notif.list_state == SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING) { + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "notif list is currently being used"); + rtn = -EBUSY; + goto out; + } + for (reg_idx = 0; reg_idx < ARRAY_SIZE(ctrl_lgrp->ctrl_notif.reg_entry); ++reg_idx) { + if ((ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].types == types) && + (ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].callback == callback)) { + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].callback = NULL; + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].tag = NULL; + ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].types = 0; + break; + } + } + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "notif callback unreg (types = 0x%X)", types); + + rtn = 0; + +out: + if (sl_ctrl_lgrp_put(ctrl_lgrp)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "notif callback unreg - lgrp removed (ctrl_lgrp = 0x%p)", ctrl_lgrp); + + return rtn; +} + +int sl_ctrl_lgrp_notif_enqueue(struct sl_ctrl_lgrp *ctrl_lgrp, u8 link_num, + u32 type, union sl_lgrp_notif_info *info, u64 info_map) +{ + struct sl_lgrp_notif_msg notif_msg; + int rtn; + + if (!ctrl_lgrp) + return 0; + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, + "notif enqueue (link_num = %d, type = 0x%X)", link_num, type); + + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + if (kfifo_is_full(&ctrl_lgrp->ctrl_notif.fifo)) { + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "notification fifo is full"); + return -ENOSPC; + } + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + + if (info) + notif_msg.info = *info; + else + memset(¬if_msg.info, 0, sizeof(notif_msg.info)); + + notif_msg.ldev_num = ctrl_lgrp->ctrl_ldev->num; + notif_msg.lgrp_num = ctrl_lgrp->num; + notif_msg.link_num = link_num; + notif_msg.type = type; + notif_msg.info_map = info_map; + + rtn = kfifo_in_spinlocked(&ctrl_lgrp->ctrl_notif.fifo, ¬if_msg, sizeof(notif_msg), + &ctrl_lgrp->ctrl_notif.lock); + if (rtn != sizeof(notif_msg)) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "write to kfifo failed"); + return -EFAULT; + } + + /* Don't catch the return as it might already be running. This is ok */ + queue_work(ctrl_lgrp->ctrl_ldev->notif_workq, &ctrl_lgrp->notif_work); + + return 0; +} + +void sl_ctrl_lgrp_notif_work(struct work_struct *notif_work) +{ + struct sl_ctrl_lgrp *ctrl_lgrp; + struct sl_lgrp_notif_msg notif_msg; + u8 reg_idx; + int rtn; + bool is_empty; + + ctrl_lgrp = container_of(notif_work, struct sl_ctrl_lgrp, notif_work); + + sl_ctrl_log_dbg(ctrl_lgrp, LOG_NAME, "notif work"); + + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + ctrl_lgrp->ctrl_notif.list_state = SL_CTRL_LGRP_NOTIF_LIST_STATE_SENDING; + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + + do { + rtn = kfifo_out_spinlocked(&ctrl_lgrp->ctrl_notif.fifo, + ¬if_msg, sizeof(notif_msg), + &ctrl_lgrp->ctrl_notif.lock); + if (rtn != sizeof(notif_msg)) { + sl_ctrl_log_err(ctrl_lgrp, LOG_NAME, "read from kfifo failed"); + goto out; + } + for (reg_idx = 0; reg_idx < SL_CTRL_LGRP_NOTIF_REG_COUNT; ++reg_idx) { + if (notif_msg.type & ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].types) + (ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].callback) + (ctrl_lgrp->ctrl_notif.reg_entry[reg_idx].tag, ¬if_msg); + } + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + is_empty = kfifo_is_empty(&ctrl_lgrp->ctrl_notif.fifo); + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); + } while (!is_empty); + +out: + spin_lock(&ctrl_lgrp->ctrl_notif.lock); + ctrl_lgrp->ctrl_notif.list_state = SL_CTRL_LGRP_NOTIF_LIST_STATE_IDLE; + spin_unlock(&ctrl_lgrp->ctrl_notif.lock); +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link.c new file mode 100644 index 000000000000..f3a8246fcb67 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link.c @@ -0,0 +1,1230 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2021-2026 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include + +#include "sl_asic.h" +#include "sl_sysfs.h" +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_link.h" +#include "data/sl_ctrl_data_link.h" +#include "sl_ctrl_link_priv.h" +#include "sl_ctrl_link_counters.h" +#include "sl_core_str.h" +#include "sl_core_link.h" +#include "sl_core_link_an.h" +#include "sl_core_mac.h" +#include "sl_core_llr.h" +#include "sl_core_link_an.h" +#include "sl_test_common.h" + +#define LOG_NAME SL_CTRL_LINK_LOG_NAME + +#define SL_CTRL_LINK_DOWN_WAIT_TIMEOUT_MS 2000 +#define SL_CTRL_LINK_DEL_WAIT_TIMEOUT_MS 2000 + +static struct sl_ctrl_link *ctrl_links[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS]; +static DEFINE_SPINLOCK(ctrl_links_lock); + +static void sl_ctrl_link_release_work(struct work_struct *work); + +int sl_ctrl_link_new(u8 ldev_num, u8 lgrp_num, u8 link_num, struct kobject *sysfs_parent) +{ + int rtn; + struct sl_ctrl_lgrp *ctrl_lgrp; + struct sl_ctrl_link *ctrl_link; + + ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, "missing lgrp (lgrp_num = %u)", lgrp_num); + return -EBADRQC; + } + + spin_lock(&ctrl_links_lock); + ctrl_link = ctrl_links[ldev_num][lgrp_num][link_num]; + spin_unlock(&ctrl_links_lock); + if (ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, "new - link exists"); + return -EBADRQC; + } + + ctrl_link = kzalloc(sizeof(struct sl_ctrl_link), GFP_KERNEL); + if (!ctrl_link) + return -ENOMEM; + + ctrl_link->magic = SL_CTRL_LINK_MAGIC; + ctrl_link->ver = SL_CTRL_LINK_VER; + ctrl_link->num = link_num; + ctrl_link->ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + + kref_init(&ctrl_link->ref_cnt); + kref_init(&ctrl_link->mem_ref); + INIT_WORK(&ctrl_link->release_work, sl_ctrl_link_release_work); + mutex_init(&ctrl_link->delete_lock); + init_completion(&ctrl_link->del_complete); + + spin_lock_init(&ctrl_link->config_lock); + spin_lock_init(&ctrl_link->data_lock); + + rtn = sl_ctrl_link_counters_init(ctrl_link); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "ctrl_link_counters_init failed [%d]", rtn); + goto out; + } + + rtn = sl_ctrl_link_cause_counters_init(ctrl_link); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "ctrl_link_cause_counters_init failed [%d]", rtn); + goto out_counters; + } + + rtn = sl_ctrl_link_an_cause_counters_init(ctrl_link); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "ctrl_link_an_cause_counters_init failed [%d]", rtn); + goto out_cause_counters; + } + + ctrl_link->up_clock.start = ktime_set(0, 0); + ctrl_link->up_clock.elapsed = ktime_set(0, 0); + ctrl_link->up_clock.attempt_count = 0; + ctrl_link->up_clock.attempt_start = ktime_set(0, 0); + ctrl_link->up_clock.attempt_elapsed = ktime_set(0, 0); + spin_lock_init(&ctrl_link->up_clock.lock); + + spin_lock_init(&ctrl_link->fec_data.lock); + timer_setup(&ctrl_link->fec_mon_timer, sl_ctrl_link_fec_mon_timer, 0); + spin_lock_init(&ctrl_link->fec_mon_timer_lock); + ctrl_link->fec_data.curr_ptr = &ctrl_link->fec_data.cntrs[0]; + ctrl_link->fec_data.prev_ptr = &ctrl_link->fec_data.cntrs[1]; + INIT_WORK(&ctrl_link->fec_mon_timer_work, sl_ctrl_link_fec_mon_timer_work); + + spin_lock_init(&ctrl_link->fec_up_cache.lock); + spin_lock_init(&ctrl_link->fec_down_cache.lock); + + ctrl_link->state = SL_LINK_STATE_DOWN; + + rtn = sl_core_link_new(ldev_num, lgrp_num, link_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "core_link_new failed [%d]", rtn); + goto out_an_counters; + } + + if (sysfs_parent) { + ctrl_link->parent_kobj = sysfs_parent; + + rtn = sl_sysfs_link_create(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "sysfs_link_create failed [%d]", rtn); + goto out_core_link; + } + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "new (link = 0x%p)", ctrl_link); + + spin_lock(&ctrl_links_lock); + ctrl_links[ldev_num][lgrp_num][link_num] = ctrl_link; + spin_unlock(&ctrl_links_lock); + + return 0; + +out_core_link: + sl_core_link_del(ldev_num, lgrp_num, link_num); +out_an_counters: + sl_ctrl_link_an_cause_counters_del(ctrl_link); +out_cause_counters: + sl_ctrl_link_cause_counters_del(ctrl_link); +out_counters: + sl_ctrl_link_counters_del(ctrl_link); +out: + kfree(ctrl_link); + return rtn; +} + +static int sl_ctrl_link_down_cmd(struct sl_ctrl_link *ctrl_link) +{ + int rtn; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down cmd"); + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN_CMD); + + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + rtn = sl_core_link_an_lp_caps_stop(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "del core_link_an_lp_caps_stop failed [%d]", rtn); + + rtn = sl_core_link_down(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, ctrl_link->num, + sl_ctrl_link_down_callback, ctrl_link, SL_LINK_DOWN_CAUSE_COMMAND_MAP); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "core_link_down failed [%d]", rtn); + return rtn; + } + + return 0; +} + +static int sl_ctrl_link_cancel_cmd(struct sl_ctrl_link *ctrl_link) +{ + int rtn; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "cancel cmd"); + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_CANCEL_CMD); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + rtn = sl_core_link_an_lp_caps_stop(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "del core_link_an_lp_caps_stop failed [%d]", rtn); + + rtn = sl_core_link_cancel(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, ctrl_link->num, + sl_ctrl_link_down_callback, ctrl_link); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "core_link_down failed [%d]", rtn); + return rtn; + } + + return 0; +} + +static void sl_ctrl_link_down_wait(struct sl_ctrl_link *ctrl_link) +{ + unsigned long timeleft; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down wait"); + + timeleft = wait_for_completion_timeout(&ctrl_link->down_complete, + msecs_to_jiffies(SL_CTRL_LINK_DOWN_WAIT_TIMEOUT_MS)); + if (timeleft == 0) + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "del completion_timeout (down_complete = 0x%p)", + &ctrl_link->down_complete); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "del completion (timeleft = %lu)", timeleft); +} + +static void sl_ctrl_link_free(struct kref *kref) +{ + kfree(container_of(kref, struct sl_ctrl_link, mem_ref)); +} + +static void sl_ctrl_link_release_work(struct work_struct *work) +{ + struct sl_ctrl_link *ctrl_link; + u32 link_state; + u8 ldev_num; + u8 lgrp_num; + u8 link_num; + int rtn; + + ctrl_link = container_of(work, struct sl_ctrl_link, release_work); + ldev_num = ctrl_link->ctrl_lgrp->ctrl_ldev->num; + lgrp_num = ctrl_link->ctrl_lgrp->num; + link_num = ctrl_link->num; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "release (link = 0x%p)", ctrl_link); + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_STARTING: + ctrl_link->is_canceled = true; + spin_unlock(&ctrl_link->data_lock); + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_CANCEL_CMD); + sl_ctrl_link_cancel_cmd(ctrl_link); + sl_ctrl_link_down_wait(ctrl_link); + break; + case SL_LINK_STATE_UP: + case SL_LINK_STATE_UP_DOWN_REQ: + ctrl_link->state = SL_LINK_STATE_STOPPING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "release stopping"); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_down_cmd(ctrl_link); + sl_ctrl_link_down_wait(ctrl_link); + break; + case SL_LINK_STATE_STOPPING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "release already stopping"); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_down_wait(ctrl_link); + break; + case SL_LINK_STATE_DOWN: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "release already down"); + spin_unlock(&ctrl_link->data_lock); + break; + case SL_LINK_STATE_INVALID: + default: + sl_ctrl_log_err(ctrl_link, LOG_NAME, "release invalid state (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + rtn = -EBUSY; + goto out_retry; + } + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + spin_unlock(&ctrl_link->data_lock); + if (link_state != SL_LINK_STATE_DOWN) { + rtn = -EBUSY; + goto out_retry; + } + + if (!ctrl_link->sysfs_deleted) { + /* Must delete sysfs first to guarantee nobody is reading */ + sl_sysfs_link_delete(ctrl_link); + ctrl_link->sysfs_deleted = true; + } + + rtn = sl_core_link_del(ldev_num, lgrp_num, link_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "release - core_link_del failed [%d]", rtn); + goto out_retry; + } + + sl_ctrl_link_counters_del(ctrl_link); + sl_ctrl_link_cause_counters_del(ctrl_link); + sl_ctrl_link_an_cause_counters_del(ctrl_link); + + spin_lock(&ctrl_links_lock); + ctrl_links[ldev_num][lgrp_num][link_num] = NULL; + spin_unlock(&ctrl_links_lock); + + ctrl_link->delete_result = 0; + complete_all(&ctrl_link->del_complete); + + kref_put(&ctrl_link->mem_ref, sl_ctrl_link_free); + return; + +out_retry: + spin_lock(&ctrl_links_lock); + ctrl_link->delete_result = rtn; + kref_init(&ctrl_link->ref_cnt); + ctrl_link->delete_requested = false; + complete_all(&ctrl_link->del_complete); + spin_unlock(&ctrl_links_lock); +} + +static void sl_ctrl_link_release(struct kref *kref) +{ + struct sl_ctrl_link *ctrl_link = container_of(kref, struct sl_ctrl_link, ref_cnt); + + queue_work(system_unbound_wq, &ctrl_link->release_work); +} + +int sl_ctrl_link_put(struct sl_ctrl_link *ctrl_link) +{ + return ctrl_link ? kref_put(&ctrl_link->ref_cnt, sl_ctrl_link_release) : 0; +} + +int sl_ctrl_link_del(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + struct sl_ctrl_link *ctrl_link; + unsigned long timeleft; + bool drop_owner = false; + int rtn; + + sl_ctrl_log_dbg(NULL, LOG_NAME, "link del (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + + spin_lock(&ctrl_links_lock); + ctrl_link = ctrl_links[ldev_num][lgrp_num][link_num]; + if (ctrl_link) + kref_get(&ctrl_link->mem_ref); + spin_unlock(&ctrl_links_lock); + if (!ctrl_link) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "link del link not found (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + mutex_lock(&ctrl_link->delete_lock); + spin_lock(&ctrl_links_lock); + if (!ctrl_link->delete_requested) { + ctrl_link->delete_requested = true; + reinit_completion(&ctrl_link->del_complete); + drop_owner = true; + } + spin_unlock(&ctrl_links_lock); + if (drop_owner) + sl_ctrl_link_put(ctrl_link); + + timeleft = wait_for_completion_timeout(&ctrl_link->del_complete, + msecs_to_jiffies(SL_CTRL_LINK_DEL_WAIT_TIMEOUT_MS)); + rtn = timeleft ? ctrl_link->delete_result : -ETIMEDOUT; + mutex_unlock(&ctrl_link->delete_lock); + kref_put(&ctrl_link->mem_ref, sl_ctrl_link_free); + return rtn; +} + +bool sl_ctrl_link_kref_get_unless_zero(struct sl_ctrl_link *ctrl_link) +{ + return (kref_get_unless_zero(&ctrl_link->ref_cnt) != 0); +} + +struct sl_ctrl_link *sl_ctrl_link_get(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + struct sl_ctrl_link *ctrl_link; + + spin_lock(&ctrl_links_lock); + ctrl_link = ctrl_links[ldev_num][lgrp_num][link_num]; + if (ctrl_link && (ctrl_link->delete_requested || + !kref_get_unless_zero(&ctrl_link->ref_cnt))) + ctrl_link = NULL; + spin_unlock(&ctrl_links_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (link = 0x%p)", ctrl_link); + + return ctrl_link; +} + +static int sl_ctrl_link_config_set_cmd(struct sl_ctrl_link *ctrl_link, struct sl_link_config *link_config) +{ + struct sl_core_link_config core_link_config; + int rtn; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "config set cmd"); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " link_up_timeout = %ums", + link_config->link_up_timeout_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " link_up_tries_max = %d", + link_config->link_up_tries_max); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_up_settle_wait = %dms", + link_config->fec_up_settle_wait_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_up_check_wait = %dms", + link_config->fec_up_check_wait_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_up_ucw_limit = %d", + link_config->fec_up_ucw_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_up_ccw_limit = %d", + link_config->fec_up_ccw_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " pml_rec_timeout = %ums", + link_config->pml_rec_timeout_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " pml_rec_rate_limit_max_time = %ums", + link_config->pml_rec_rate_limit_max_time_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " pml_rec_rate_limit_window_size = %ums", + link_config->pml_rec_rate_limit_window_size_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " pause_map = 0x%X", + link_config->pause_map); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " hpe_map = 0x%X", + link_config->hpe_map); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " options = 0x%X", + link_config->options); + + /* Check if the configuration is locked. If we are performing an admin operation, then we can ignore the + * lock bit and continue to write the config. + */ + spin_lock(&ctrl_link->config_lock); + if (!(link_config->options & SL_LINK_CONFIG_OPT_ADMIN) && + (ctrl_link->config.options & SL_LINK_CONFIG_OPT_LOCK)) { + sl_ctrl_log_info(ctrl_link, LOG_NAME, "link config locked (options: 0x%X)", ctrl_link->config.options); + spin_unlock(&ctrl_link->config_lock); + return 0; + } + spin_unlock(&ctrl_link->config_lock); + + /* Admin bit is transient */ + link_config->options &= ~SL_LINK_CONFIG_OPT_ADMIN; + + if ((!link_config->fec_up_settle_wait_ms && link_config->fec_up_check_wait_ms) || + (link_config->fec_up_settle_wait_ms && !link_config->fec_up_check_wait_ms)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "fec_up invalid (settle_wait = %ums, check_wait = %ums)", + link_config->fec_up_settle_wait_ms, link_config->fec_up_check_wait_ms); + return -EINVAL; + } + + core_link_config.magic = SL_CORE_LINK_CONFIG_MAGIC; + core_link_config.fault_start_callback = sl_ctrl_link_fault_start_callback; + core_link_config.fault_callback = sl_ctrl_link_fault_callback; + + /* timeouts */ + core_link_config.link_up_timeout_ms = link_config->link_up_timeout_ms; + + /* fec config */ + core_link_config.fec_up_settle_wait_ms = link_config->fec_up_settle_wait_ms; + core_link_config.fec_up_check_wait_ms = link_config->fec_up_check_wait_ms; + core_link_config.fec_up_ucw_limit = link_config->fec_up_ucw_limit; + core_link_config.fec_up_ccw_limit = link_config->fec_up_ccw_limit; + + /* pml recovery config */ + core_link_config.pml_rec_timeout_ms = link_config->pml_rec_timeout_ms; + core_link_config.pml_rec_rate_limit_max_time_ms = link_config->pml_rec_rate_limit_max_time_ms; + core_link_config.pml_rec_rate_limit_window_size_ms = link_config->pml_rec_rate_limit_window_size_ms; + + /* maps */ + core_link_config.hpe_map = link_config->hpe_map; + core_link_config.pause_map = link_config->pause_map; + + /* flags */ + core_link_config.flags = link_config->options; + + rtn = sl_core_link_config_set(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &core_link_config); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "core_link_config_set failed [%d]", rtn); + return rtn; + } + + spin_lock(&ctrl_link->config_lock); + ctrl_link->config = *link_config; + spin_unlock(&ctrl_link->config_lock); + + if (ctrl_link->config.link_up_tries_max == SL_LINK_INFINITE_UP_TRIES) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link up tries set to infinite"); + + return 0; +} + +int sl_ctrl_link_config_set(u8 ldev_num, u8 lgrp_num, u8 link_num, + struct sl_link_config *link_config) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + u32 link_state; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "config set NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "config set link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + rtn = sl_ctrl_data_link_state_get(ctrl_link, &link_state); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "data_link_state_get failed [%d]", rtn); + goto out; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "config set (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + + switch (link_state) { + case SL_LINK_STATE_DOWN: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "config - set"); + rtn = sl_ctrl_link_config_set_cmd(ctrl_link, link_config); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "link_config_set_cmd failed [%d]", rtn); + goto out; + } + + rtn = 0; + goto out; + case SL_LINK_STATE_STARTING: + case SL_LINK_STATE_INVALID: + case SL_LINK_STATE_STOPPING: + case SL_LINK_STATE_UP: + case SL_LINK_STATE_UP_DOWN_REQ: + default: + sl_ctrl_log_err(ctrl_link, LOG_NAME, "config - invalid (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + rtn = -EBADRQC; + goto out; + } + +out: + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "config - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +int sl_ctrl_link_policy_set(u8 ldev_num, u8 lgrp_num, u8 link_num, + struct sl_link_policy *link_policy) +{ + int rtn; + u32 link_state; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + struct sl_core_link_policy core_link_policy; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "policy set NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "policy set link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + /* Check if the policy is locked. If we are performing an admin operation, then we can ignore the + * lock bit and continue to write the config. + */ + spin_lock(&ctrl_link->config_lock); + if (!(link_policy->options & SL_LINK_POLICY_OPT_ADMIN) && + (ctrl_link->policy.options & SL_LINK_POLICY_OPT_LOCK)) { + sl_ctrl_log_info(ctrl_link, LOG_NAME, "link policy locked (options: 0x%X)", ctrl_link->policy.options); + spin_unlock(&ctrl_link->config_lock); + rtn = 0; + goto out; + } + spin_unlock(&ctrl_link->config_lock); + + /* Admin bit is transient */ + link_policy->options &= ~SL_LINK_POLICY_OPT_ADMIN; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "policy set"); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_mon_ucw_down_limit = %d", link_policy->fec_mon_ucw_down_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_mon_ucw_warn_limit = %d", link_policy->fec_mon_ucw_warn_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_mon_ccw_down_limit = %d", link_policy->fec_mon_ccw_down_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_mon_ccw_warn_limit = %d", link_policy->fec_mon_ccw_warn_limit); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " fec_mon_period = %dms", link_policy->fec_mon_period_ms); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, " options = 0x%X", link_policy->options); + + spin_lock(&ctrl_link->config_lock); + ctrl_link->policy = *link_policy; + spin_unlock(&ctrl_link->config_lock); + + core_link_policy.options = link_policy->options; + rtn = sl_core_link_policy_set(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, ctrl_link->num, + &core_link_policy); + if (rtn) { + sl_core_log_err(ctrl_link, LOG_NAME, "core link policy set failed [%d]", rtn); + rtn = -EBADRQC; + goto out; + } + + rtn = sl_ctrl_data_link_state_get(ctrl_link, &link_state); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "data link state get failed [%d]", rtn); + goto out; + } + + if ((link_state != SL_LINK_STATE_UP) && (link_state != SL_LINK_STATE_UP_DOWN_REQ)) { + sl_core_log_dbg(ctrl_link, LOG_NAME, "link_policy_set not up (link_state = %u %s)", link_state, + sl_core_link_state_str(link_state)); + rtn = 0; + goto out; + } + + sl_ctrl_link_fec_mon_start(ctrl_link); + + rtn = 0; + +out: + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "policy set - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +int sl_ctrl_link_an_lp_caps_get(u8 ldev_num, u8 lgrp_num, u8 link_num, + struct sl_link_caps *caps, u32 timeout_ms, u32 flags) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "caps get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "caps get link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps get"); + + rtn = sl_core_link_an_lp_caps_get(ldev_num, lgrp_num, link_num, + sl_ctrl_link_an_lp_caps_get_callback, ctrl_link, caps, timeout_ms, flags); + if (rtn) + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "core_link_an_lp_caps_get failed [%d]", rtn); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps get - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +int sl_ctrl_link_an_lp_caps_stop(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "caps stop NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "caps stop link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps stop"); + + rtn = sl_core_link_an_lp_caps_stop(ldev_num, lgrp_num, link_num); + if (rtn) + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "core_link_an_lp_caps_stop failed [%d]", rtn); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps stop - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +static int sl_ctrl_link_up_cmd(struct sl_ctrl_link *ctrl_link) +{ + int rtn; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up cmd"); + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_CMD); + + sl_ctrl_link_up_clock_start(ctrl_link); + + sl_ctrl_link_is_canceled_set(ctrl_link, false); + + init_completion(&ctrl_link->down_complete); + + sl_ctrl_link_up_clock_attempt_start(ctrl_link); + + rtn = sl_core_link_up(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, ctrl_link->num, + sl_ctrl_link_up_callback, ctrl_link); + if (rtn) { + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_STOPPING); + + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "core_link_up failed [%d]", rtn); + + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + + return rtn; + } + + return 0; +} + +int sl_ctrl_link_up(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + u32 link_state; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "link up NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "link up ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up"); + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_DOWN: + ctrl_link->state = SL_LINK_STATE_STARTING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - starting"); + spin_unlock(&ctrl_link->data_lock); + rtn = sl_ctrl_link_up_cmd(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "link_up_cmd failed [%d]", rtn); + goto out; + } + + rtn = 0; + goto out; + case SL_LINK_STATE_STARTING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - already starting"); + spin_unlock(&ctrl_link->data_lock); + rtn = -EINPROGRESS; + goto out; + case SL_LINK_STATE_UP: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - already up"); + spin_unlock(&ctrl_link->data_lock); + rtn = -EALREADY; + goto out; + case SL_LINK_STATE_UP_DOWN_REQ: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - already up down requested"); + spin_unlock(&ctrl_link->data_lock); + rtn = -EALREADY; + goto out; + case SL_LINK_STATE_STOPPING: + case SL_LINK_STATE_INVALID: + default: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - invalid (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + rtn = -EBADRQC; + goto out; + } + +out: + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +int sl_ctrl_link_down(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + u32 link_state; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_dbg(NULL, LOG_NAME, + "link down NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "link down ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down"); + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_STARTING: + ctrl_link->state = SL_LINK_STATE_STOPPING; + ctrl_link->is_canceled = true; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - canceling"); + spin_unlock(&ctrl_link->data_lock); + rtn = sl_ctrl_link_cancel_cmd(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "cancel_cmd failed [%d]", rtn); + goto out; + } + + rtn = 0; + goto out; + case SL_LINK_STATE_UP: + case SL_LINK_STATE_UP_DOWN_REQ: + ctrl_link->state = SL_LINK_STATE_STOPPING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - stopping"); + spin_unlock(&ctrl_link->data_lock); + rtn = sl_ctrl_link_down_cmd(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "down_cmd failed [%d]", rtn); + goto out; + } + + rtn = 0; + goto out; + case SL_LINK_STATE_DOWN: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - already down"); + spin_unlock(&ctrl_link->data_lock); + rtn = -EALREADY; + goto out; + case SL_LINK_STATE_STOPPING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - already stopping"); + spin_unlock(&ctrl_link->data_lock); + rtn = -EINPROGRESS; + goto out; + case SL_LINK_STATE_INVALID: + default: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - invalid (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + rtn = -EBADRQC; + goto out; + } + +out: + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +static int sl_ctrl_link_reset_cmd(struct sl_ctrl_link *ctrl_link) +{ + int rtn; + u8 ldev_num; + u8 lgrp_num; + u8 link_num; + unsigned long timeleft; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset cmd"); + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_RESET_CMD); + + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + ldev_num = ctrl_link->ctrl_lgrp->ctrl_ldev->num; + lgrp_num = ctrl_link->ctrl_lgrp->num; + link_num = ctrl_link->num; + + rtn = sl_core_llr_stop(ldev_num, lgrp_num, link_num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "core_llr_stop failed [%d]", rtn); + + rtn = sl_core_mac_tx_stop(ldev_num, lgrp_num, link_num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "core_mac_tx_stop failed [%d]", rtn); + + rtn = sl_core_mac_rx_stop(ldev_num, lgrp_num, link_num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "core_mac_rx_stop failed [%d]", rtn); + + rtn = sl_core_link_down(ldev_num, lgrp_num, link_num, sl_ctrl_link_down_callback, + ctrl_link, SL_LINK_DOWN_CAUSE_COMMAND_MAP); + if (rtn) + sl_ctrl_log_warn(ctrl_link, LOG_NAME, "core_link_down failed [%d]", rtn); + + timeleft = wait_for_completion_timeout(&ctrl_link->down_complete, + msecs_to_jiffies(SL_CTRL_LINK_DOWN_WAIT_TIMEOUT_MS)); + if (timeleft == 0) + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "completion_timeout (down_complete = 0x%p)", &ctrl_link->down_complete); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "completion (timeleft = %lu)", timeleft); + + sl_core_link_reset(ldev_num, lgrp_num, link_num); + + return 0; +} + +int sl_ctrl_link_reset(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + u32 link_state; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "link reset NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "link reset ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset"); + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_STARTING: + ctrl_link->is_canceled = true; + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_CANCEL_CMD); + fallthrough; + case SL_LINK_STATE_UP: + case SL_LINK_STATE_UP_DOWN_REQ: + ctrl_link->state = SL_LINK_STATE_STOPPING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset - stopping"); + spin_unlock(&ctrl_link->data_lock); + rtn = sl_ctrl_link_reset_cmd(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "link_reset_cmd failed [%d]", rtn); + goto out; + } + + rtn = 0; + goto out; + case SL_LINK_STATE_DOWN: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset - already down"); + spin_unlock(&ctrl_link->data_lock); + rtn = 0; + goto out; + case SL_LINK_STATE_STOPPING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down - already stopping"); + spin_unlock(&ctrl_link->data_lock); + + rtn = 0; + goto out; + case SL_LINK_STATE_INVALID: + default: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset - invalid (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + rtn = -EBADRQC; + goto out; + } + +out: + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "reset - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +int sl_ctrl_link_up_clocks_get(u8 ldev_num, u8 lgrp_num, u8 link_num, + s64 *attempt_time, s64 *total_time, s64 *up_time) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "clocks get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "clocks get link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + spin_lock(&ctrl_link->up_clock.lock); + + if (!ktime_compare(ctrl_link->up_clock.attempt_start, ktime_set(0, 0))) + *attempt_time = ktime_to_ms(ctrl_link->up_clock.attempt_elapsed); + else + *attempt_time = ktime_to_ms(ktime_sub(ktime_get(), ctrl_link->up_clock.attempt_start)); + + if (!ktime_compare(ctrl_link->up_clock.start, ktime_set(0, 0))) + *total_time = ktime_to_ms(ctrl_link->up_clock.elapsed); + else + *total_time = ktime_to_ms(ktime_sub(ktime_get(), ctrl_link->up_clock.start)); + + if (!ktime_compare(ctrl_link->up_clock.up, ktime_set(0, 0))) + *up_time = 0; + else + *up_time = ktime_to_ms(ktime_sub(ktime_get(), ctrl_link->up_clock.up)); + + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "clocks get (attempt_time = %lld, total_time = %lld)", *attempt_time, *total_time); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "clocks get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_up_count_get(u8 ldev_num, u8 lgrp_num, u8 link_num, u32 *attempt_count) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "count get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "count get link ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + spin_lock(&ctrl_link->up_clock.lock); + *attempt_count = ctrl_link->up_clock.attempt_count; + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "count get (attempt_count = %u)", *attempt_count); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "count get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_state_get_cmd(u8 ldev_num, u8 lgrp_num, u8 link_num, u32 *state) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + *state = SL_LINK_STATE_INVALID; + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_dbg(NULL, LOG_NAME, + "state get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return 0; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "state get kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + rtn = sl_ctrl_data_link_state_get(ctrl_link, state); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "data_link_state_get failed [%d]", rtn); + goto out; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "state get (state = %u %s)", + *state, sl_link_state_str(*state)); + +out: + sl_ctrl_link_put(ctrl_link); + + return rtn; +} + +int sl_ctrl_link_an_lp_caps_state_get(u8 ldev_num, u8 lgrp_num, u8 link_num, u32 *state) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "an lp caps state get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EINVAL; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "an lp caps state get kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + *state = sl_core_link_an_lp_caps_state_get(ldev_num, lgrp_num, link_num); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps state get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_an_fail_cause_get(u8 ldev_num, u8 lgrp_num, u8 link_num, u32 *fail_cause, time64_t *fail_time) +{ + int rtn; + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "an fail cause get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "an fail cause get kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + rtn = sl_core_link_an_fail_cause_get(ldev_num, lgrp_num, link_num, fail_cause, fail_time); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an fail cause get - link removed (link = 0x%p)", ctrl_link); + + return rtn; +} + +u32 sl_ctrl_link_an_retry_count_get(struct sl_ctrl_link *ctrl_link, int *count) +{ + *count = sl_core_link_an_retry_count_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an retry get (count = %d)", *count); + + return 0; +} + +int sl_ctrl_link_info_map_get(u8 ldev_num, u8 lgrp_num, u8 link_num, u64 *info_map) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info map get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "info map get ref unavailable (ctrl_link = 0x%p)", + ctrl_link); + return -EBADRQC; + } + + *info_map = sl_core_link_info_map_get(ldev_num, lgrp_num, link_num); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "info map get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_counters.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_counters.c new file mode 100644 index 000000000000..1de7d93033b9 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_counters.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2023-2026 Hewlett Packard Enterprise Development LP */ + +#include + +#include "sl_ctrl_link.h" +#include "sl_ctrl_link_counters.h" +#include "base/sl_ctrl_log.h" +#include "hw/sl_core_hw_an.h" + +#define LOG_NAME SL_CTRL_LINK_LOG_NAME + +#define SL_CTRL_LINK_COUNTER_INIT(_link, _counter) \ + (_link)->counters[_counter].name = #_counter + +#define SL_CTRL_LINK_CAUSE_COUNTER_INIT(_link, _counter) \ + (_link)->cause_counters[_counter].name = #_counter + +#define SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(_link, _counter) \ + (_link)->an_cause_counters[_counter].name = #_counter + +int sl_ctrl_link_counters_init(struct sl_ctrl_link *ctrl_link) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link counters init"); + + ctrl_link->counters = kzalloc(sizeof(struct sl_ctrl_link_counter) * SL_CTRL_LINK_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_link->counters) + return -ENOMEM; + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_CMD); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_RETRY); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_FAIL); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN_CMD); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_CANCEL_CMD); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_CANCELED); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_RESET_CMD); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_FAULT_ASYNC); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_RECOVERING); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_CCW_WARN_CROSSED); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UCW_WARN_CROSSED); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN_CCW_LIMIT_CROSSED); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN_UCW_LIMIT_CROSSED); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN_CCW_CAUSE); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_DOWN_UCW_CAUSE); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_FAIL_CCW_LIMIT_CROSSED); + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_UP_FAIL_UCW_LIMIT_CROSSED); + + SL_CTRL_LINK_COUNTER_INIT(ctrl_link, LINK_HW_AN_ATTEMPT); + + return 0; +} + +int sl_ctrl_link_counters_get(struct sl_ctrl_link *ctrl_link, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_link->counters[counter].count); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (counter = %u %s, count = %d)", counter, + ctrl_link->counters[counter].name, *count); + + return 0; +} + +void sl_ctrl_link_counters_del(struct sl_ctrl_link *ctrl_link) +{ + kfree(ctrl_link->counters); +} + +int sl_ctrl_link_cause_counters_init(struct sl_ctrl_link *ctrl_link) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link cause counters init"); + + ctrl_link->cause_counters = kzalloc(sizeof(*ctrl_link->cause_counters) * + SL_CTRL_LINK_CAUSE_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_link->cause_counters) + return -ENOMEM; + + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UCW); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_LF); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_RF); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_DOWN); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UP_TRIES); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_AUTONEG_NOMATCH); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_AUTONEG); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_CONFIG); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_INTR_ENABLE); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_TIMEOUT); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_CANCELED); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UNSUPPORTED_CABLE); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_COMMAND); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_DOWNSHIFT); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_LLR_REPLAY_MAX); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UPSHIFT); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_AUTONEG_CONFIG); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_PCS_FAULT); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_SERDES_PLL); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_SERDES_CONFIG); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_SERDES_SIGNAL); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_SERDES_QUALITY); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_NO_MEDIA); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_CCW); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_MEDIA_HOT); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_MEDIA_WARM); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_INTR_REGISTER); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_MEDIA_ERROR); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UP_CANCELED); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_UNSUPPORTED_SPEED); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_SS200_CABLE); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_TX_LOL); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_RX_LOL); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_TX_LOS); + SL_CTRL_LINK_CAUSE_COUNTER_INIT(ctrl_link, LINK_CAUSE_RX_LOS); + + return 0; +} + +int sl_ctrl_link_cause_counters_get(struct sl_ctrl_link *ctrl_link, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_link->cause_counters[counter].count); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (cause counter = %u %s, count = %d)", counter, + ctrl_link->cause_counters[counter].name, *count); + + return 0; +} + +void sl_ctrl_link_cause_counters_del(struct sl_ctrl_link *ctrl_link) +{ + kfree(ctrl_link->cause_counters); +} + +int sl_ctrl_link_an_cause_counters_init(struct sl_ctrl_link *ctrl_link) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link an cause counters init"); + + ctrl_link->an_cause_counters = kzalloc(sizeof(*ctrl_link->an_cause_counters) * + SL_CTRL_LINK_AN_CAUSE_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_link->an_cause_counters) + return -ENOMEM; + + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_LP_CAPS_SERDES_LINK_UP_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_LP_CAPS_NOT_COMPLETE); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_NOT_COMPLETE); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_TEST_CAPS_NOMATCH); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_SERDES_LINK_UP_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_BP_STORE_STATE_BAD); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_BP_STORE_LP_ABILITY_NOT_SET); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_BP_STORE_STATE_ERROR); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_BP_STORE_BP_NOT_SET); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_BP_SEND_INTR_ENABLE_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_NP_STORE_STATE_BAD); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_NP_STORE_BP_SET); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_NP_CHECK_STATE_BAD); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_INTR_STATE_INVALID); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_INTR_AN_RETRY_NP_SEND_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_INTR_OUT_OF_PAGES); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_INTR_NP_SEND_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_FAIL); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_NO_BP); + SL_CTRL_LINK_AN_CAUSE_COUNTER_INIT(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_OUI_INVALID); + + return 0; +} + +int sl_ctrl_link_an_cause_counters_get(struct sl_ctrl_link *ctrl_link, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_link->an_cause_counters[counter].count); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "get (an cause counter = %u %s, count = %d)", counter, + ctrl_link->an_cause_counters[counter].name, *count); + + return 0; +} + +void sl_ctrl_link_an_cause_counters_del(struct sl_ctrl_link *ctrl_link) +{ + kfree(ctrl_link->an_cause_counters); +} + +void sl_ctrl_link_cause_counter_inc(struct sl_ctrl_link *ctrl_link, u64 cause_map) +{ + unsigned long which; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link cause counter inc"); + + for_each_set_bit(which, (unsigned long *)&cause_map, sizeof(cause_map) * BITS_PER_BYTE) { + switch (BIT(which)) { + case SL_LINK_DOWN_CAUSE_UCW: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UCW); + break; + case SL_LINK_DOWN_CAUSE_LF: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_LF); + break; + case SL_LINK_DOWN_CAUSE_RF: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_RF); + break; + case SL_LINK_DOWN_CAUSE_DOWN: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_DOWN); + break; + case SL_LINK_DOWN_CAUSE_UP_TRIES: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UP_TRIES); + break; + case SL_LINK_DOWN_CAUSE_AUTONEG_NOMATCH: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_AUTONEG_NOMATCH); + break; + case SL_LINK_DOWN_CAUSE_AUTONEG: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_AUTONEG); + break; + case SL_LINK_DOWN_CAUSE_CONFIG: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_CONFIG); + break; + case SL_LINK_DOWN_CAUSE_INTR_ENABLE: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_INTR_ENABLE); + break; + case SL_LINK_DOWN_CAUSE_TIMEOUT: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_TIMEOUT); + break; + case SL_LINK_DOWN_CAUSE_CANCELED: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_CANCELED); + break; + case SL_LINK_DOWN_CAUSE_UNSUPPORTED_CABLE: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UNSUPPORTED_CABLE); + break; + case SL_LINK_DOWN_CAUSE_COMMAND: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_COMMAND); + break; + case SL_LINK_DOWN_CAUSE_DOWNSHIFT: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_DOWNSHIFT); + break; + case SL_LINK_DOWN_CAUSE_LLR_REPLAY_MAX: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_LLR_REPLAY_MAX); + break; + case SL_LINK_DOWN_CAUSE_UPSHIFT: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UPSHIFT); + break; + case SL_LINK_DOWN_CAUSE_AUTONEG_CONFIG: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_AUTONEG_CONFIG); + break; + case SL_LINK_DOWN_CAUSE_PCS_FAULT: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_PCS_FAULT); + break; + case SL_LINK_DOWN_CAUSE_SERDES_PLL: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_SERDES_PLL); + break; + case SL_LINK_DOWN_CAUSE_SERDES_CONFIG: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_SERDES_CONFIG); + break; + case SL_LINK_DOWN_CAUSE_SERDES_SIGNAL: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_SERDES_SIGNAL); + break; + case SL_LINK_DOWN_CAUSE_SERDES_QUALITY: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_SERDES_QUALITY); + break; + case SL_LINK_DOWN_CAUSE_NO_MEDIA: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_NO_MEDIA); + break; + case SL_LINK_DOWN_CAUSE_CCW: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_CCW); + break; + case SL_LINK_DOWN_CAUSE_MEDIA_HOT: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_MEDIA_HOT); + break; + case SL_LINK_DOWN_CAUSE_MEDIA_WARM: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_MEDIA_WARM); + break; + case SL_LINK_DOWN_CAUSE_INTR_REGISTER: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_INTR_REGISTER); + break; + case SL_LINK_DOWN_CAUSE_MEDIA_ERROR: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_MEDIA_ERROR); + break; + case SL_LINK_DOWN_CAUSE_UP_CANCELED: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UP_CANCELED); + break; + case SL_LINK_DOWN_CAUSE_UNSUPPORTED_SPEED: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_UNSUPPORTED_SPEED); + break; + case SL_LINK_DOWN_CAUSE_SS200_CABLE: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_SS200_CABLE); + break; + case SL_LINK_DOWN_CAUSE_TX_LOL: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_TX_LOL); + break; + case SL_LINK_DOWN_CAUSE_RX_LOL: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_RX_LOL); + break; + case SL_LINK_DOWN_CAUSE_TX_LOS: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_TX_LOS); + break; + case SL_LINK_DOWN_CAUSE_RX_LOS: + SL_CTRL_LINK_CAUSE_COUNTER_INC(ctrl_link, LINK_CAUSE_RX_LOS); + break; + case SL_LINK_DOWN_RETRYABLE: + case SL_LINK_DOWN_ORIGIN_ASYNC: + case SL_LINK_DOWN_ORIGIN_LINK_UP: + break; + default: + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "cause_counter_inc unknown cause (bit = %lu)", which); + break; + } + } +} + +void sl_ctrl_link_an_cause_counter_inc(struct sl_ctrl_link *ctrl_link, unsigned long cause_map) +{ + unsigned long which; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link an cause counter inc"); + + BUILD_BUG_ON(SL_CTRL_LINK_AN_CAUSE_COUNTERS_COUNT > 32); + + for_each_set_bit(which, &cause_map, sizeof(cause_map) * BITS_PER_BYTE) { + switch (BIT(which)) { + case SL_CORE_HW_AN_FAIL_CAUSE_LP_CAPS_SERDES_LINK_UP_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_LP_CAPS_SERDES_LINK_UP_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_LP_CAPS_NOT_COMPLETE: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_LP_CAPS_NOT_COMPLETE); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_NOT_COMPLETE: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_NOT_COMPLETE); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_TEST_CAPS_NOMATCH: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_TEST_CAPS_NOMATCH); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_SERDES_LINK_UP_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_SERDES_LINK_UP_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_BP_STORE_STATE_BAD: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_BP_STORE_STATE_BAD); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_BP_STORE_LP_ABILITY_NOT_SET: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_BP_STORE_LP_ABILITY_NOT_SET); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_BP_STORE_STATE_ERROR: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_BP_STORE_STATE_ERROR); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_BP_STORE_BP_NOT_SET: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_BP_STORE_BP_NOT_SET); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_BP_SEND_INTR_ENABLE_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_BP_SEND_INTR_ENABLE_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_NP_STORE_STATE_BAD: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_NP_STORE_STATE_BAD); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_NP_STORE_BP_SET: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_NP_STORE_BP_SET); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_NP_CHECK_STATE_BAD: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_NP_CHECK_STATE_BAD); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_INTR_STATE_INVALID: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_INTR_STATE_INVALID); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_INTR_AN_RETRY_NP_SEND_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_INTR_AN_RETRY_NP_SEND_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_INTR_OUT_OF_PAGES: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_INTR_OUT_OF_PAGES); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_INTR_NP_SEND_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_INTR_NP_SEND_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_PAGES_DECODE_FAIL: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_FAIL); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_PAGES_DECODE_NO_BP: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_NO_BP); + break; + case SL_CORE_HW_AN_FAIL_CAUSE_PAGES_DECODE_OUI_INVALID: + SL_CTRL_LINK_AN_CAUSE_COUNTER_INC(ctrl_link, LINK_AN_CAUSE_PAGES_DECODE_OUI_INVALID); + break; + default: + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "an cause_counter_inc unknown fault cause (bit = %lu)", which); + break; + } + } +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec.c new file mode 100644 index 000000000000..2c54795281e0 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */ + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_link.h" +#include "sl_ctrl_link_fec.h" +#include "sl_core_link.h" + +#define LOG_NAME SL_CTRL_LINK_FEC_LOG_NAME + +int sl_ctrl_link_fec_info_get(u8 ldev_num, u8 lgrp_num, u8 link_num, struct sl_fec_info *fec_info) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + int x; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "fec info get kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + *fec_info = sl_ctrl_link_fec_data_info_get(ctrl_link); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "info get (ucw = %llu, ccw = %llu, gcw = %llu, period = %ums)", + fec_info->ucw, fec_info->ccw, fec_info->gcw, fec_info->period_ms); + for (x = 0; x < SL_CTRL_NUM_FEC_LANES; ++x) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "info get (lane %d = 0x%llX)", x, fec_info->lanes[x]); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fec info get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_fec_tail_get(u8 ldev_num, u8 lgrp_num, u8 link_num, struct sl_fec_tail *fec_tail) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + int x; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "tail get NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "fec tail get kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + *fec_tail = sl_ctrl_link_fec_data_tail_get(ctrl_link); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "tail get (period = %ums))", fec_tail->period_ms); + for (x = 0; x < SL_CTRL_NUM_CCW_BINS; ++x) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "tail get (bin %d = %llu)", x, fec_tail->ccw_bins[x]); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fec tail get - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_fec_data_get(u8 ldev_num, u8 lgrp_num, u8 link_num, + struct sl_core_link_fec_cw_cntrs *cw_cntrs, + struct sl_core_link_fec_lane_cntrs *lane_cntrs, + struct sl_core_link_fec_tail_cntrs *tail_cntrs) +{ + struct sl_core_link *held_link_1 __free(sl_core_link_put) = NULL; + held_link_1 = sl_core_link_get(ldev_num, lgrp_num, link_num); + if (!held_link_1) + return -ENODEV; + return sl_core_link_fec_data_get(held_link_1, cw_cntrs, lane_cntrs, + tail_cntrs); +} + +int sl_ctrl_link_fec_mon_state_get(struct sl_ctrl_link *ctrl_link, u32 *state) +{ + spin_lock(&ctrl_link->fec_mon_timer_lock); + *state = ctrl_link->fec_mon_state; + spin_unlock(&ctrl_link->fec_mon_timer_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fec monitor (state = %u, %s)", *state, + sl_ctrl_link_fec_mon_state_str(*state)); + + return 0; +} + +const char *sl_ctrl_link_fec_mon_state_str(u32 state) +{ + switch (state) { + case SL_CTRL_LINK_FEC_MON_OFF: + return "off"; + case SL_CTRL_LINK_FEC_MON_ON: + return "on"; + default: + return "unknown"; + } +} + +//#define SL_CTRL_SERDES_RATE_25 25781250000 +//#define SL_CTRL_SERDES_RATE_50 53125000000 +//#define SL_CTRL_SERDES_RATE_100 106250000000 + +/* CCW BER Calculation Multiplier (10^6). */ +#define SL_CTRL_LINK_FEC_CCW_BER_MULT 1000000 +#define SL_CTRL_LINK_FEC_CCW_BER_EXP 6 + +/* UCW BER Calculation Multiplier (10^12). */ +#define SL_CTRL_LINK_FEC_UCW_BER_MULT 1000000000000 +#define SL_CTRL_LINK_FEC_UCW_BER_EXP 12 + +#define MAX_CCW_COUNT (U64_MAX / (HZ * SL_CTRL_LINK_FEC_CCW_BER_MULT)) +#define MAX_UCW_COUNT (U64_MAX / (HZ * SL_CTRL_LINK_FEC_UCW_BER_MULT)) + +int sl_ctrl_link_fec_ber_calc(struct sl_fec_info *fec_info, struct sl_ber *ucw_ber, struct sl_ber *ccw_ber) +{ + u64 total; + u64 ccw_rate; /* Rate * 10^10 */ + u64 ucw_rate; /* Rate * 10^10 */ + int mag; + + sl_ctrl_log_dbg(NULL, LOG_NAME, "fec_ber_calc"); + + /* Total must be > 0 to calculate */ + total = fec_info->ccw + fec_info->gcw; + if (total == 0) { + sl_ctrl_log_err(NULL, LOG_NAME, + "fec invalid (total = %llu)", total); + return -EINVAL; + } + + if (fec_info->ccw > MAX_CCW_COUNT) { + sl_ctrl_log_err(NULL, LOG_NAME, + "fec ccw invalid (%llu > %llu)", fec_info->ccw, MAX_CCW_COUNT); + return -EINVAL; + } + + if (fec_info->ucw > MAX_UCW_COUNT) { + sl_ctrl_log_err(NULL, LOG_NAME, + "fec ucw invalid (%llu > %llu)", fec_info->ucw, MAX_UCW_COUNT); + return -EINVAL; + } + + /* B = BER + * n = [U/C]CW count + * g = Good CW count + * T = total CW = ccw_count + g + * t = time = (jiffies / HZ) + * + * B = (n/T) / t; B = (n * HZ) / (T * jiffies) + */ + + /* We use RS(544/514) with 10 bit symbols which is a total of 5440 bits per CW. */ + total = fec_info->ccw + (fec_info->gcw * 5440); + + /* CCW count * CCW multiplier */ + ccw_rate = (fec_info->ccw * HZ * SL_CTRL_LINK_FEC_CCW_BER_MULT) / (total * fec_info->period_ms); + + /* Keep at least 2 significant digits */ + mag = 0; + while ((ccw_rate / 100) > 9) { + ccw_rate /= 100; + mag += 2; + } + + ccw_ber->mant = ccw_rate; + ccw_ber->exp = mag - SL_CTRL_LINK_FEC_CCW_BER_EXP; /* Divide by CCW multiplier */ + + /* UCW count * UCW multiplier */ + ucw_rate = (fec_info->ucw * HZ * SL_CTRL_LINK_FEC_UCW_BER_MULT) / (total * fec_info->period_ms); + + /* Keep at least 2 significant digits */ + mag = 0; + while ((ucw_rate / 100) > 9) { + ucw_rate /= 100; + mag += 2; + } + + ucw_ber->mant = ucw_rate; + ucw_ber->exp = mag - SL_CTRL_LINK_FEC_UCW_BER_EXP; /* Divide by UCW multiplier */ + + sl_ctrl_log_dbg(NULL, LOG_NAME, "fec_ber_calc (CCW = %ue%d, UCW = %ue%d)", + ccw_ber->mant, ccw_ber->exp, ucw_ber->mant, ucw_ber->exp); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec_priv.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec_priv.c new file mode 100644 index 000000000000..a1f50d1a3b3c --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_fec_priv.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2023-2026 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_lgrp_notif.h" +#include "sl_ctrl_link.h" +#include "sl_ctrl_link_priv.h" +#include "sl_ctrl_link_counters.h" +#include "sl_ctrl_link_fec.h" +#include "sl_ctrl_link_fec_priv.h" +#include "sl_core_link.h" +#include "sl_platform.h" + +#define SL_CTRL_LINK_UCW_LIMIT_MAX_CHANCES 2 +#define SL_CTRL_LINK_CCW_LIMIT_MAX_CHANCES 2 +#define SL_CTRL_LINK_FEC_MON_PERIOD_MS 500 +#define LOG_NAME SL_CTRL_LINK_FEC_LOG_NAME + +void sl_ctrl_link_fec_data_store(struct sl_ctrl_link *ctrl_link, + struct sl_core_link_fec_cw_cntrs *cw_cntrs, + struct sl_core_link_fec_lane_cntrs *lane_cntrs, + struct sl_core_link_fec_tail_cntrs *tail_cntrs) +{ + struct sl_ctrl_link_fec_cntrs *tmp_cntrs_ptr; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "data_store"); + + spin_lock(&ctrl_link->fec_data.lock); + + tmp_cntrs_ptr = ctrl_link->fec_data.prev_ptr; + ctrl_link->fec_data.prev_ptr = ctrl_link->fec_data.curr_ptr; + ctrl_link->fec_data.curr_ptr = tmp_cntrs_ptr; + + ctrl_link->fec_data.curr_ptr->cw_cntrs = *cw_cntrs; + ctrl_link->fec_data.curr_ptr->lane_cntrs = *lane_cntrs; + ctrl_link->fec_data.curr_ptr->tail_cntrs = *tail_cntrs; + ctrl_link->fec_data.curr_ptr->timestamp = jiffies; + + spin_unlock(&ctrl_link->fec_data.lock); +} + +void sl_ctrl_link_fec_up_cache_clear(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->fec_up_cache.lock); + memset(&(ctrl_link->fec_up_cache.cw_cntrs), 0, sizeof(struct sl_core_link_fec_cw_cntrs)); + memset(&(ctrl_link->fec_up_cache.lane_cntrs), 0, sizeof(struct sl_core_link_fec_lane_cntrs)); + memset(&(ctrl_link->fec_up_cache.tail_cntrs), 0, sizeof(struct sl_core_link_fec_tail_cntrs)); + spin_unlock(&ctrl_link->fec_up_cache.lock); +} + +void sl_ctrl_link_fec_up_cache_store(struct sl_ctrl_link *ctrl_link, + struct sl_core_link_fec_cw_cntrs *cw_cntrs, + struct sl_core_link_fec_lane_cntrs *lane_cntrs, + struct sl_core_link_fec_tail_cntrs *tail_cntrs) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up_cache_store"); + + spin_lock(&ctrl_link->fec_up_cache.lock); + ctrl_link->fec_up_cache.cw_cntrs = *cw_cntrs; + ctrl_link->fec_up_cache.lane_cntrs = *lane_cntrs; + ctrl_link->fec_up_cache.tail_cntrs = *tail_cntrs; + spin_unlock(&ctrl_link->fec_up_cache.lock); +} + +void sl_ctrl_link_fec_down_cache_clear(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->fec_down_cache.lock); + memset(&(ctrl_link->fec_down_cache.cw_cntrs), 0, sizeof(struct sl_core_link_fec_cw_cntrs)); + memset(&(ctrl_link->fec_down_cache.lane_cntrs), 0, sizeof(struct sl_core_link_fec_lane_cntrs)); + memset(&(ctrl_link->fec_down_cache.tail_cntrs), 0, sizeof(struct sl_core_link_fec_tail_cntrs)); + spin_unlock(&ctrl_link->fec_down_cache.lock); +} + +void sl_ctrl_link_fec_down_cache_store(struct sl_ctrl_link *ctrl_link, + struct sl_core_link_fec_cw_cntrs *cw_cntrs, + struct sl_core_link_fec_lane_cntrs *lane_cntrs, + struct sl_core_link_fec_tail_cntrs *tail_cntrs) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down_cache_store"); + + spin_lock(&ctrl_link->fec_down_cache.lock); + ctrl_link->fec_down_cache.cw_cntrs = *cw_cntrs; + ctrl_link->fec_down_cache.lane_cntrs = *lane_cntrs; + ctrl_link->fec_down_cache.tail_cntrs = *tail_cntrs; + spin_unlock(&ctrl_link->fec_down_cache.lock); +} + +static void sl_ctrl_link_fec_mon_limits_calc(struct sl_ctrl_link *ctrl_link) +{ + struct sl_link_policy link_policy; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "mon limits calc"); + + sl_ctrl_link_policy_get(ctrl_link, &link_policy); + + spin_lock(&ctrl_link->fec_data.lock); + + ctrl_link->fec_data.info.monitor.period_ms = (link_policy.fec_mon_period_ms < 0) ? + SL_CTRL_LINK_FEC_MON_PERIOD_MS : link_policy.fec_mon_period_ms; + + if (link_policy.fec_mon_ccw_down_limit < 0) + ctrl_link->fec_data.info.monitor.ccw_down_limit = 0; + else + ctrl_link->fec_data.info.monitor.ccw_down_limit = link_policy.fec_mon_ccw_down_limit; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "mon limits calc (ccw_down_limit = %d)", + ctrl_link->fec_data.info.monitor.ccw_down_limit); + + if (link_policy.fec_mon_ccw_warn_limit < 0) + ctrl_link->fec_data.info.monitor.ccw_warn_limit = + sl_ctrl_link_fec_limit_calc(ctrl_link, + SL_CTRL_LINK_FEC_CCW_MANT, SL_CTRL_LINK_FEC_CCW_EXP) >> 1; + else + ctrl_link->fec_data.info.monitor.ccw_warn_limit = link_policy.fec_mon_ccw_warn_limit; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "mon limits calc (ccw_warn_limit = %d)", + ctrl_link->fec_data.info.monitor.ccw_warn_limit); + + if (ctrl_link->fec_data.info.monitor.ccw_down_limit && + (ctrl_link->fec_data.info.monitor.ccw_warn_limit > ctrl_link->fec_data.info.monitor.ccw_down_limit)) + sl_ctrl_log_warn(ctrl_link, LOG_NAME, + "CCW warning limit set greater than down limit (%d > %d)", + ctrl_link->fec_data.info.monitor.ccw_warn_limit, + ctrl_link->fec_data.info.monitor.ccw_down_limit); + + if (link_policy.fec_mon_ucw_down_limit < 0) { + ctrl_link->fec_data.info.monitor.ucw_down_limit = + sl_ctrl_link_fec_limit_calc(ctrl_link, SL_CTRL_LINK_FEC_UCW_MANT, SL_CTRL_LINK_FEC_UCW_EXP); + } else { + ctrl_link->fec_data.info.monitor.ucw_down_limit = link_policy.fec_mon_ucw_down_limit; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "mon limits calc (ucw_down_limit = %d)", + ctrl_link->fec_data.info.monitor.ucw_down_limit); + + if (link_policy.fec_mon_ucw_warn_limit < 0) + ctrl_link->fec_data.info.monitor.ucw_warn_limit = + sl_ctrl_link_fec_limit_calc(ctrl_link, SL_CTRL_LINK_FEC_UCW_MANT, + SL_CTRL_LINK_FEC_UCW_EXP) >> 1; + else + ctrl_link->fec_data.info.monitor.ucw_warn_limit = link_policy.fec_mon_ucw_warn_limit; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "mon limits calc (ucw_warn_limit = %d)", + ctrl_link->fec_data.info.monitor.ucw_warn_limit); + + if (ctrl_link->fec_data.info.monitor.ucw_down_limit && + (ctrl_link->fec_data.info.monitor.ucw_warn_limit > ctrl_link->fec_data.info.monitor.ucw_down_limit)) + sl_ctrl_log_warn(ctrl_link, LOG_NAME, + "CCW warning limit set greater than down limit (%d > %d)", + ctrl_link->fec_data.info.monitor.ucw_warn_limit, + ctrl_link->fec_data.info.monitor.ucw_down_limit); + + spin_unlock(&ctrl_link->fec_data.lock); +} + +void sl_ctrl_link_fec_mon_start(struct sl_ctrl_link *ctrl_link) +{ + u32 period; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "mon_start"); + + sl_ctrl_link_fec_mon_limits_calc(ctrl_link); + + spin_lock(&ctrl_link->fec_data.lock); + period = ctrl_link->fec_data.info.monitor.period_ms; + spin_unlock(&ctrl_link->fec_data.lock); + if (!period) { + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + return; + } + + spin_lock(&ctrl_link->fec_mon_timer_lock); + ctrl_link->fec_mon_timer_stop = false; + ctrl_link->fec_mon_state = SL_CTRL_LINK_FEC_MON_ON; + mod_timer(&ctrl_link->fec_mon_timer, jiffies + msecs_to_jiffies(period)); + spin_unlock(&ctrl_link->fec_mon_timer_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor timer started (period = %ums)", period); +} + +void sl_ctrl_link_fec_data_calc(struct sl_ctrl_link *ctrl_link) +{ + int x; + + spin_lock(&ctrl_link->fec_data.lock); + + ctrl_link->fec_data.info.ccw = + ctrl_link->fec_data.curr_ptr->cw_cntrs.ccw - ctrl_link->fec_data.prev_ptr->cw_cntrs.ccw; + ctrl_link->fec_data.info.ucw = + ctrl_link->fec_data.curr_ptr->cw_cntrs.ucw - ctrl_link->fec_data.prev_ptr->cw_cntrs.ucw; + ctrl_link->fec_data.info.gcw = + ctrl_link->fec_data.curr_ptr->cw_cntrs.gcw - ctrl_link->fec_data.prev_ptr->cw_cntrs.gcw; + for (x = 0; x < SL_CORE_LINK_FEC_NUM_LANES; ++x) + ctrl_link->fec_data.info.lanes[x] = ctrl_link->fec_data.curr_ptr->lane_cntrs.lanes[x] - + ctrl_link->fec_data.prev_ptr->lane_cntrs.lanes[x]; + ctrl_link->fec_data.info.period_ms = + jiffies_to_msecs(ctrl_link->fec_data.curr_ptr->timestamp - ctrl_link->fec_data.prev_ptr->timestamp); + + for (x = 0; x < SL_CTRL_NUM_CCW_BINS; ++x) + ctrl_link->fec_data.tail.ccw_bins[x] = ctrl_link->fec_data.curr_ptr->tail_cntrs.ccw_bins[x] - + ctrl_link->fec_data.prev_ptr->tail_cntrs.ccw_bins[x]; + ctrl_link->fec_data.tail.period_ms = ctrl_link->fec_data.info.period_ms; + + spin_unlock(&ctrl_link->fec_data.lock); + + if ((ctrl_link->fec_data.info.ccw > 0) || (ctrl_link->fec_data.info.ucw > 0)) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "data_calc (ccw = %llu, ucw = %llu, gcw = %llu, period = %ums)", + ctrl_link->fec_data.info.ccw, ctrl_link->fec_data.info.ucw, + ctrl_link->fec_data.info.gcw, ctrl_link->fec_data.info.period_ms); + for (x = 0; x < SL_CTRL_NUM_CCW_BINS; ++x) + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "data_calc (bin %d = %llu)", x, ctrl_link->fec_data.tail.ccw_bins[x]); + } +} + +struct sl_fec_info sl_ctrl_link_fec_data_info_get(struct sl_ctrl_link *ctrl_link) +{ + struct sl_fec_info fec_info; + + spin_lock(&ctrl_link->fec_data.lock); + fec_info = ctrl_link->fec_data.info; + spin_unlock(&ctrl_link->fec_data.lock); + + return fec_info; +} + +struct sl_fec_tail sl_ctrl_link_fec_data_tail_get(struct sl_ctrl_link *ctrl_link) +{ + struct sl_fec_tail fec_tail; + + spin_lock(&ctrl_link->fec_data.lock); + fec_tail = ctrl_link->fec_data.tail; + spin_unlock(&ctrl_link->fec_data.lock); + + return fec_tail; +} + +static bool sl_ctrl_link_fec_ucw_chance_limit_check(struct sl_ctrl_link *ctrl_link, s32 limit, struct sl_fec_info *info) +{ + if (SL_CTRL_LINK_FEC_UCW_LIMIT_CHECK(limit, info)) { + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN_UCW_LIMIT_CROSSED); + ctrl_link->fec_ucw_chance = ctrl_link->fec_ucw_chance + 1; + } else { + ctrl_link->fec_ucw_chance = 0; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fec_ucw_chance_limit_check (limit = %d, UCW = %llu, ucw_chance = %u)", + limit, info->ucw, ctrl_link->fec_ucw_chance); + + return ctrl_link->fec_ucw_chance > SL_CTRL_LINK_UCW_LIMIT_MAX_CHANCES; +} + +static bool sl_ctrl_link_fec_ccw_chance_limit_check(struct sl_ctrl_link *ctrl_link, s32 limit, struct sl_fec_info *info) +{ + if (SL_CTRL_LINK_FEC_CCW_LIMIT_CHECK(limit, info)) { + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN_CCW_LIMIT_CROSSED); + ctrl_link->fec_ccw_chance = ctrl_link->fec_ccw_chance + 1; + } else { + ctrl_link->fec_ccw_chance = 0; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fec_ccw_chance_limit_check (limit = %d, CCW = %llu, ccw_chance = %u)", + limit, info->ccw, ctrl_link->fec_ccw_chance); + + return ctrl_link->fec_ccw_chance > SL_CTRL_LINK_CCW_LIMIT_MAX_CHANCES; +} + +int sl_ctrl_link_fec_data_check(struct sl_ctrl_link *ctrl_link) +{ + int rtn; + s32 ccw_down_limit; + s32 ccw_warn_limit; + s32 ucw_down_limit; + s32 ucw_warn_limit; + struct sl_fec_info fec_info; + bool is_limit_crossed; + time64_t limit_crossed_time; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "data check"); + + spin_lock(&ctrl_link->fec_data.lock); + ccw_down_limit = ctrl_link->fec_data.info.monitor.ccw_down_limit; + ccw_warn_limit = ctrl_link->fec_data.info.monitor.ccw_warn_limit; + ucw_down_limit = ctrl_link->fec_data.info.monitor.ucw_down_limit; + ucw_warn_limit = ctrl_link->fec_data.info.monitor.ucw_warn_limit; + spin_unlock(&ctrl_link->fec_data.lock); + + fec_info = ctrl_link->fec_data.info; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "data check config (ccw_down = %d, ucw_down = %d, ccw_warn = %d, ucw_warn = %d)", + ccw_down_limit, ucw_down_limit, ccw_warn_limit, ucw_warn_limit); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "data check info (UCW = %llu, CCW = %llu, GCW = %llu, period = %ums)", + fec_info.ucw, fec_info.ccw, fec_info.gcw, fec_info.period_ms); + + if (sl_ctrl_link_fec_ucw_chance_limit_check(ctrl_link, ucw_down_limit, &fec_info)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "UCW exceeded down limit (UCW = %llu, CCW = %llu, ucw_chance = %u)", + fec_info.ucw, fec_info.ccw, ctrl_link->fec_ucw_chance); + ctrl_link->fec_ucw_chance = 0; + rtn = sl_ctrl_link_async_down(ctrl_link, SL_LINK_DOWN_CAUSE_UCW_MAP, false); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "data check UCW ctrl_link_async_down failed [%d]", rtn); + return rtn; + } + return -EIO; + } + + if (sl_ctrl_link_fec_ccw_chance_limit_check(ctrl_link, ccw_down_limit, &fec_info)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "CCW exceeded down limit (UCW = %llu, CCW = %llu, ccw_chance = %u)", + fec_info.ucw, fec_info.ccw, ctrl_link->fec_ccw_chance); + ctrl_link->fec_ccw_chance = 0; + rtn = sl_ctrl_link_async_down(ctrl_link, SL_LINK_DOWN_CAUSE_CCW_MAP, false); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "data check CCW ctrl_link_async_down failed [%d]", rtn); + return rtn; + } + return -EIO; + } + + if (SL_CTRL_LINK_FEC_UCW_LIMIT_CHECK(ucw_warn_limit, &fec_info)) { + rtn = sl_core_link_ucw_warn_limit_crossed_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &is_limit_crossed, &limit_crossed_time); + if (rtn) + sl_core_log_warn_trace(ctrl_link, LOG_NAME, "ucw_warn_limit_crossed_get failed [%d]", rtn); + + if (!is_limit_crossed) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "UCW exceeded warn limit (UCW = %llu, CCW = %llu)", + fec_info.ucw, fec_info.ccw); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_UCW_WARN, NULL, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "data check ctrl_lgrp_notif_enqueue failed [%d]", rtn); + } + sl_core_link_ucw_warn_limit_crossed_set(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, true); + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UCW_WARN_CROSSED); + } + + if (SL_CTRL_LINK_FEC_CCW_LIMIT_CHECK(ccw_warn_limit, &fec_info)) { + rtn = sl_core_link_ccw_warn_limit_crossed_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &is_limit_crossed, &limit_crossed_time); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "ccw_warn_limit_crossed_get failed [%d]", rtn); + + if (!is_limit_crossed) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "CCW exceeded warn limit (UCW = %llu, CCW = %llu)", + fec_info.ucw, fec_info.ccw); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_CCW_WARN, NULL, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "data check ctrl_lgrp_notif_enqueue failed [%d]", rtn); + } + sl_core_link_ccw_warn_limit_crossed_set(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, true); + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_CCW_WARN_CROSSED); + } + + return 0; +} + +void sl_ctrl_link_fec_mon_timer_work(struct work_struct *work) +{ + struct sl_core_link *held_link_1 __free(sl_core_link_put) = NULL; + int rtn; + struct sl_ctrl_link *ctrl_link; + u32 period; + bool stop; + struct sl_core_link_fec_cw_cntrs cw_cntrs; + struct sl_core_link_fec_lane_cntrs lane_cntrs; + struct sl_core_link_fec_tail_cntrs tail_cntrs; + + ctrl_link = container_of(work, struct sl_ctrl_link, fec_mon_timer_work); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor timer work"); + + spin_lock(&ctrl_link->fec_data.lock); + period = ctrl_link->fec_data.info.monitor.period_ms; + spin_unlock(&ctrl_link->fec_data.lock); + if (!period) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor period zero"); + return; + } + + held_link_1 = sl_core_link_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num); + if (!held_link_1) + return; + if (sl_core_link_is_pml_recovery_running(held_link_1)) { + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "fec monitor skipped due to pml recovery running"); + goto start_mon; + } + + spin_lock(&ctrl_link->fec_mon_timer_lock); + stop = ctrl_link->fec_mon_timer_stop; + spin_unlock(&ctrl_link->fec_mon_timer_lock); + if (stop) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor stopped"); + return; + } + + rtn = sl_ctrl_link_fec_data_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &cw_cntrs, &lane_cntrs, + &tail_cntrs); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "core_link_fec_tail_cntrs_get failed [%d]", rtn); + goto start_mon; + } + + sl_ctrl_link_fec_data_store(ctrl_link, &cw_cntrs, &lane_cntrs, &tail_cntrs); + + sl_ctrl_link_fec_data_calc(ctrl_link); + + rtn = sl_ctrl_link_fec_data_check(ctrl_link); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, "check failed [%d]", rtn); + return; + } + + spin_lock(&ctrl_link->fec_data.lock); + period = ctrl_link->fec_data.info.monitor.period_ms; + spin_unlock(&ctrl_link->fec_data.lock); + if (!period) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor period zero"); + return; + } + + spin_lock(&ctrl_link->fec_mon_timer_lock); + stop = ctrl_link->fec_mon_timer_stop; + spin_unlock(&ctrl_link->fec_mon_timer_lock); + if (stop) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor stopped"); + return; + } + +start_mon: + mod_timer(&ctrl_link->fec_mon_timer, jiffies + msecs_to_jiffies(period)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor started"); +} + +void sl_ctrl_link_fec_mon_timer(struct timer_list *timer) +{ + struct sl_ctrl_link *ctrl_link; + + ctrl_link = timer_container_of(ctrl_link, timer, fec_mon_timer); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor timer"); + + queue_work(ctrl_link->ctrl_lgrp->ctrl_ldev->workq, &ctrl_link->fec_mon_timer_work); +} + +void sl_ctrl_link_fec_mon_stop(struct sl_ctrl_link *ctrl_link) +{ + bool stop; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor stop"); + + spin_lock(&ctrl_link->fec_mon_timer_lock); + stop = ctrl_link->fec_mon_timer_stop; + ctrl_link->fec_mon_timer_stop = true; + ctrl_link->fec_mon_state = SL_CTRL_LINK_FEC_MON_OFF; + spin_unlock(&ctrl_link->fec_mon_timer_lock); + + if (stop) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "monitor already stopped"); + return; + } + + timer_delete_sync(&ctrl_link->fec_mon_timer); +} + +#define SL_CTRL_LINK_FEC_LIMIT_25 25781250000ULL +#define SL_CTRL_LINK_FEC_LIMIT_50 53125000000ULL +#define SL_CTRL_LINK_FEC_LIMIT_100 106250000000ULL +u32 sl_ctrl_link_fec_limit_calc(struct sl_ctrl_link *ctrl_link, u32 mant, int exp) +{ + u64 limit; + int x; + struct sl_link_caps link_caps; + + sl_core_link_caps_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &link_caps); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "fec limit calc (tech_map = 0x%X, mant = %u, exp = %d)", + link_caps.tech_map, mant, exp); + + if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_CK_400G) + limit = SL_CTRL_LINK_FEC_LIMIT_100 << 2; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_CK_200G) + limit = SL_CTRL_LINK_FEC_LIMIT_100 << 1; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_BS_200G) + limit = SL_CTRL_LINK_FEC_LIMIT_50 << 2; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_CK_100G) + limit = SL_CTRL_LINK_FEC_LIMIT_100; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_BJ_100G) + limit = SL_CTRL_LINK_FEC_LIMIT_25 << 2; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_CD_100G) + limit = SL_CTRL_LINK_FEC_LIMIT_50 << 1; + else if (link_caps.tech_map & SL_LGRP_CONFIG_TECH_CD_50G) + limit = SL_CTRL_LINK_FEC_LIMIT_50; + else + limit = SL_CTRL_LINK_FEC_LIMIT_100; + + limit *= mant; + for (x = exp; x < 0; ++x) + limit /= 10; + + if (limit > INT_MAX) + return INT_MAX; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "fec limit calc (limit = %llu)", limit); + + return limit; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_priv.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_priv.c new file mode 100644 index 000000000000..252673b57a0b --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_link_priv.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2021-2026 Hewlett Packard Enterprise Development LP */ + +#include + +#include + +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_lgrp_notif.h" +#include "sl_ctrl_link.h" +#include "sl_ctrl_link_priv.h" +#include "sl_ctrl_link_counters.h" +#include "sl_core_link.h" +#include "sl_core_link_an.h" +#include "sl_core_str.h" + +#define LOG_NAME SL_CTRL_LINK_LOG_NAME + +#define SL_LINK_DOWN_CAUSE_FATAL_MASK ( \ + SL_LINK_DOWN_CAUSE_AUTONEG_NOMATCH | \ + SL_LINK_DOWN_CAUSE_CONFIG | \ + SL_LINK_DOWN_CAUSE_DOWNSHIFT | \ + SL_LINK_DOWN_CAUSE_INTR_ENABLE | \ + SL_LINK_DOWN_CAUSE_UNSUPPORTED_CABLE | \ + SL_LINK_DOWN_CAUSE_SS200_CABLE | \ + SL_LINK_DOWN_CAUSE_UCW | \ + SL_LINK_DOWN_CAUSE_MEDIA_HOT | \ + SL_LINK_DOWN_CAUSE_MEDIA_WARM | \ + SL_LINK_DOWN_CAUSE_NO_MEDIA | \ + SL_LINK_DOWN_CAUSE_UPSHIFT) + +void sl_ctrl_link_is_canceled_set(struct sl_ctrl_link *ctrl_link, bool canceled) +{ + spin_lock(&ctrl_link->data_lock); + ctrl_link->is_canceled = canceled; + spin_unlock(&ctrl_link->data_lock); +} + +bool sl_ctrl_link_is_canceled(struct sl_ctrl_link *ctrl_link) +{ + bool is_canceled; + + spin_lock(&ctrl_link->data_lock); + is_canceled = ctrl_link->is_canceled; + spin_unlock(&ctrl_link->data_lock); + + return is_canceled; +} + +static void sl_ctrl_link_last_up_fail_cause_map_clr(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->data_lock); + ctrl_link->last_up_fail_cause_map = 0; + ctrl_link->last_up_fail_time = 0; + spin_unlock(&ctrl_link->data_lock); +} + +static void sl_ctrl_link_last_up_fail_cause_map_set(struct sl_ctrl_link *ctrl_link, u64 last_up_fail_cause_map) +{ + spin_lock(&ctrl_link->data_lock); + ctrl_link->last_up_fail_cause_map |= last_up_fail_cause_map; + ctrl_link->last_up_fail_time = ktime_get_real_seconds(); + spin_unlock(&ctrl_link->data_lock); +} + +static bool sl_ctrl_link_is_last_up_fail_cause_set(struct sl_ctrl_link *ctrl_link, u64 last_up_fail_cause) +{ + bool is_last_up_fail_cause_set; + + spin_lock(&ctrl_link->data_lock); + is_last_up_fail_cause_set = ctrl_link->last_up_fail_cause_map & last_up_fail_cause; + spin_unlock(&ctrl_link->data_lock); + + return is_last_up_fail_cause_set; +} + +static int sl_ctrl_link_up_notif_send(struct sl_ctrl_lgrp *ctrl_lgrp, struct sl_ctrl_link *ctrl_link, + u64 info_map, u32 speed, u32 fec_mode, u32 fec_type) +{ + union sl_lgrp_notif_info info; + + info.link_up.mode = speed; + info.link_up.fec_mode = fec_mode; + info.link_up.fec_type = fec_type; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up notif send (speed = %u %s, fec mode = %u %s, fec type = %u)", + speed, sl_lgrp_config_tech_str(speed), fec_mode, sl_lgrp_fec_mode_str(fec_mode), fec_type); + + return sl_ctrl_lgrp_notif_enqueue(ctrl_lgrp, ctrl_link->num, SL_LGRP_NOTIF_LINK_UP, + &info, info_map); +} + +static int sl_ctrl_link_up_fail_notif_send(struct sl_ctrl_lgrp *ctrl_lgrp, struct sl_ctrl_link *ctrl_link, + u64 cause_map, u64 info_map) +{ + union sl_lgrp_notif_info info; + + info.cause_map = cause_map; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up fail notif send (cause_map = 0x%llX)", cause_map); + + return sl_ctrl_lgrp_notif_enqueue(ctrl_lgrp, ctrl_link->num, SL_LGRP_NOTIF_LINK_UP_FAIL, + &info, info_map); +} + +static int sl_ctrl_async_link_down_notif_send(struct sl_ctrl_link *ctrl_link, u64 cause_map, u64 info_map) +{ + union sl_lgrp_notif_info info; + char cause_str[SL_LINK_DOWN_CAUSE_STR_SIZE]; + + info.cause_map = cause_map; + + sl_link_down_cause_map_with_info_str(cause_map, cause_str, sizeof(cause_str)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "async_link_down_notif_send (core_cause_map = 0x%llX %s)", cause_map, cause_str); + + return sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_ASYNC_DOWN, &info, info_map); +} + +static int sl_ctrl_link_down_notif_send(struct sl_ctrl_link *ctrl_link, u64 cause_map, u64 info_map) +{ + union sl_lgrp_notif_info info; + char cause_str[SL_LINK_DOWN_CAUSE_STR_SIZE]; + + info.cause_map = cause_map; + + sl_link_down_cause_map_with_info_str(cause_map, cause_str, sizeof(cause_str)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "link_down_notif_send (core_cause_map = 0x%llX %s)", cause_map, cause_str); + + return sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_DOWN, &info, info_map); +} + +static int sl_ctrl_link_down_req_notif_send(struct sl_ctrl_link *ctrl_link, u64 cause_map, u64 info_map) +{ + union sl_lgrp_notif_info info; + char cause_str[SL_LINK_DOWN_CAUSE_STR_SIZE]; + + info.cause_map = cause_map; + + sl_link_down_cause_map_with_info_str(cause_map, cause_str, sizeof(cause_str)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "link_down_req_notif_send (core_cause_map = 0x%llX %s)", cause_map, cause_str); + + return sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_DOWN_REQ, &info, info_map); +} + +void sl_ctrl_link_up_clock_start(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->up_clock.lock); + ctrl_link->up_clock.start = ktime_get(); + ctrl_link->up_clock.elapsed = ktime_set(0, 0); + ctrl_link->up_clock.attempt_count = 0; + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "clock start (time = %lld)", ctrl_link->up_clock.start); +} + +static void sl_ctrl_link_up_clock_stop(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->up_clock.lock); + ctrl_link->up_clock.elapsed = ktime_sub(ktime_get(), ctrl_link->up_clock.start); + ctrl_link->up_clock.start = ktime_set(0, 0); + ctrl_link->up_clock.up = ktime_get(); + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "clock stop (time = %lld)", ctrl_link->up_clock.elapsed); +} + +void sl_ctrl_link_up_clock_reset(struct sl_ctrl_link *ctrl_link) +{ + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "clock reset"); + + spin_lock(&ctrl_link->up_clock.lock); + ctrl_link->up_clock.elapsed = ktime_set(0, 0); + ctrl_link->up_clock.start = ktime_set(0, 0); + ctrl_link->up_clock.attempt_count = 0; + ctrl_link->up_clock.attempt_elapsed = ktime_set(0, 0); + ctrl_link->up_clock.attempt_start = ktime_set(0, 0); + ctrl_link->up_clock.up = ktime_set(0, 0); + spin_unlock(&ctrl_link->up_clock.lock); +} + +void sl_ctrl_link_up_clock_attempt_start(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->up_clock.lock); + ctrl_link->up_clock.attempt_start = ktime_get(); + ctrl_link->up_clock.attempt_elapsed = ktime_set(0, 0); + ctrl_link->up_clock.attempt_count++; + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "clock attempt start (time = %lld)", ctrl_link->up_clock.attempt_start); +} + +static void sl_ctrl_link_up_clock_attempt_stop(struct sl_ctrl_link *ctrl_link) +{ + spin_lock(&ctrl_link->up_clock.lock); + ctrl_link->up_clock.attempt_elapsed = ktime_sub(ktime_get(), ctrl_link->up_clock.attempt_start); + ctrl_link->up_clock.attempt_start = ktime_set(0, 0); + spin_unlock(&ctrl_link->up_clock.lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "clock attempt stop (time = %lld)", ctrl_link->up_clock.attempt_elapsed); +} + +static void sl_ctrl_link_state_stopping_set(struct sl_ctrl_link *ctrl_link) +{ + u32 link_state; + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_STOPPING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "stopping set - already stopping"); + spin_unlock(&ctrl_link->data_lock); + return; + case SL_LINK_STATE_STARTING: + case SL_LINK_STATE_UP: + case SL_LINK_STATE_UP_DOWN_REQ: + ctrl_link->state = SL_LINK_STATE_STOPPING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "stopping set - stopping"); + spin_unlock(&ctrl_link->data_lock); + return; + default: + sl_ctrl_log_err(ctrl_link, LOG_NAME, "stopping set - invalid state (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + return; + } +} + +int sl_ctrl_link_up_callback(void *tag, struct sl_core_link_up_info *up_info) +{ + struct sl_ctrl_link *ctrl_link; + char core_imap_str[SL_LINK_INFO_STRLEN]; + int max_up_tries; + u32 up_count; + int rtn; + s64 attempt_time; + s64 total_time; + s64 up_time; + union sl_lgrp_notif_info info; + struct sl_core_link_up_info core_link_up_info; + u64 up_fail_cause_map; + + ctrl_link = tag; + core_link_up_info = *up_info; + + sl_core_info_map_str(core_link_up_info.info_map, core_imap_str, sizeof(core_imap_str)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "up callback (core_state = %u %s, core_cause_map = 0x%llX, info_map = %s (0x%llx))", + core_link_up_info.state, sl_core_link_state_str(core_link_up_info.state), + core_link_up_info.cause_map, core_imap_str, core_link_up_info.info_map); + + sl_ctrl_link_last_up_fail_cause_map_clr(ctrl_link); + sl_ctrl_link_last_up_fail_cause_map_set(ctrl_link, core_link_up_info.cause_map); + + sl_ctrl_link_up_clock_attempt_stop(ctrl_link); + + switch (core_link_up_info.state) { + case SL_CORE_LINK_STATE_UP: + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP); + sl_ctrl_link_up_clock_stop(ctrl_link); + + rtn = sl_ctrl_link_up_clocks_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, + ctrl_link->num, &attempt_time, &total_time, &up_time); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "link_up_clocks_get failed [%d]", rtn); + + rtn = sl_ctrl_link_up_count_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, + ctrl_link->num, &up_count); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "link_up_count_get failed [%d]", rtn); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "up callback (count = %d, attempt_time = %lldms, total_time = %lldms)", + up_count, attempt_time, total_time); + + sl_ctrl_link_fec_mon_start(ctrl_link); + + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_UP); + + rtn = sl_ctrl_link_up_notif_send(ctrl_link->ctrl_lgrp, ctrl_link, core_link_up_info.info_map, + core_link_up_info.speed, core_link_up_info.fec_mode, core_link_up_info.fec_type); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback ctrl_link_up_notif_send failed [%d]", rtn); + return 0; + + case SL_CORE_LINK_STATE_DOWN: + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_FAIL); + + spin_lock(&ctrl_link->config_lock); + max_up_tries = ctrl_link->config.link_up_tries_max; + spin_unlock(&ctrl_link->config_lock); + + /* canceled */ + if (sl_ctrl_link_is_canceled(ctrl_link)) { + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up retry canceled"); + + sl_ctrl_link_state_stopping_set(ctrl_link); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + complete_all(&ctrl_link->down_complete); + + rtn = sl_ctrl_link_last_up_fail_cause_map_get(ctrl_link, &up_fail_cause_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "last_up_fail_cause_map_get failed [%d]", rtn); + + rtn = sl_ctrl_link_up_fail_notif_send(ctrl_link->ctrl_lgrp, ctrl_link, + up_fail_cause_map, core_link_up_info.info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback work ctrl_link_up_fail_notif_send failed [%d]", rtn); + + return 0; + } + + /* check up tries */ + rtn = sl_ctrl_link_up_count_get(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, &up_count); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "link_up_count_get failed [%d]", rtn); + + if ((up_count >= max_up_tries) && (max_up_tries != SL_LINK_INFINITE_UP_TRIES)) { + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up callback work out of up tries"); + + sl_ctrl_link_state_stopping_set(ctrl_link); + + sl_ctrl_link_last_up_fail_cause_map_set(ctrl_link, SL_LINK_DOWN_CAUSE_UP_TRIES_MAP); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + + complete_all(&ctrl_link->down_complete); + + rtn = sl_ctrl_link_last_up_fail_cause_map_get(ctrl_link, &up_fail_cause_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "last_up_fail_cause_map_get failed [%d]", rtn); + rtn = sl_ctrl_link_up_fail_notif_send(ctrl_link->ctrl_lgrp, ctrl_link, + up_fail_cause_map, core_link_up_info.info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback work ctrl_link_up_fail_notif_send failed [%d]", rtn); + return 0; + } + + /* check fatal down causes */ + if (sl_ctrl_link_is_last_up_fail_cause_set(ctrl_link, SL_LINK_DOWN_CAUSE_FATAL_MASK)) { + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "up callback work fatal down cause"); + + sl_ctrl_link_state_stopping_set(ctrl_link); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + complete_all(&ctrl_link->down_complete); + + rtn = sl_ctrl_link_last_up_fail_cause_map_get(ctrl_link, &up_fail_cause_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, "last_up_fail_cause_map_get failed [%d]", rtn); + rtn = sl_ctrl_link_up_fail_notif_send(ctrl_link->ctrl_lgrp, ctrl_link, + up_fail_cause_map, core_link_up_info.info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback work ctrl_link_up_fail_notif_send failed [%d]", rtn); + + return 0; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "link up retry (ctrl_link = 0x%p)", ctrl_link); + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_RETRY); + + sl_ctrl_link_up_clock_attempt_start(ctrl_link); + + rtn = sl_core_link_up(ctrl_link->ctrl_lgrp->ctrl_ldev->num, ctrl_link->ctrl_lgrp->num, + ctrl_link->num, sl_ctrl_link_up_callback, ctrl_link); + if (rtn) { + sl_ctrl_link_up_clock_reset(ctrl_link); + + sl_ctrl_link_state_stopping_set(ctrl_link); + + sl_ctrl_log_err_trace(ctrl_link, LOG_NAME, + "up callback work core_link_up failed [%d]", rtn); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + + complete_all(&ctrl_link->down_complete); + + info.error = rtn; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_ERROR, &info, core_link_up_info.info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback work ctrl_lgrp_notif_enqueue failed [%d]", rtn); + + return info.error; + } + return 0; + + default: + sl_ctrl_link_up_clock_reset(ctrl_link); + sl_ctrl_link_state_stopping_set(ctrl_link); + + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "up callback work invalid (core_state = %u, core_imap = %s)", + core_link_up_info.state, core_imap_str); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + + complete_all(&ctrl_link->down_complete); + + info.error = -EBADRQC; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_ERROR, &info, core_link_up_info.info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "up callback work ctrl_lgrp_notif_enqueue failed [%d]", rtn); + + return info.error; + } +} + +int sl_ctrl_link_fault_start_callback(u8 ldev_num, u8 lgrp_num, u8 link_num) +{ + struct sl_ctrl_link *ctrl_link __free(sl_ctrl_link_put) = NULL; + + ctrl_link = sl_ctrl_link_get(ldev_num, lgrp_num, link_num); + if (!ctrl_link) + return -ENODEV; + if (!ctrl_link) { + sl_ctrl_log_err(NULL, LOG_NAME, + "NULL link (ldev_num = %u, lgrp_num = %u, link_num = %u)", + ldev_num, lgrp_num, link_num); + return -EBADRQC; + } + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "fault start callback kref unavailable (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "fault start callback"); + + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_STOPPING); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + if (sl_ctrl_link_put(ctrl_link)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "fault start callback - link removed (link = 0x%p)", ctrl_link); + + return 0; +} + +int sl_ctrl_link_fault_callback(void *tag, u32 core_state, u64 core_cause_map, u64 core_info_map) +{ + struct sl_ctrl_link *ctrl_link; + char core_imap_str[SL_LINK_INFO_STRLEN]; + int rtn; + union sl_lgrp_notif_info info; + + ctrl_link = tag; + + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_FAULT_ASYNC); + + sl_core_info_map_str(core_info_map, core_imap_str, sizeof(core_imap_str)); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "fault callback (core_state = %u %s, core_cause_map = 0x%llX, core_info_map = %s (0x%llx))", + core_state, sl_core_link_state_str(core_state), core_cause_map, core_imap_str, core_info_map); + + sl_ctrl_link_up_clock_reset(ctrl_link); + + switch (core_state) { + case SL_CORE_LINK_STATE_DOWN: + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + complete_all(&ctrl_link->down_complete); + + rtn = sl_ctrl_async_link_down_notif_send(ctrl_link, core_cause_map, core_info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fault callback async_link_down_notif_send failed [%d]", rtn); + + return 0; + + default: + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "fault callback invalid (core_state = %u)", core_state); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + + complete_all(&ctrl_link->down_complete); + + info.error = -EBADRQC; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_LINK_ERROR, &info, core_info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "fault callback ctrl_lgrp_notif_enqueue failed [%d]", rtn); + + return 0; + } +} + +int sl_ctrl_link_an_lp_caps_get_callback(void *tag, struct sl_link_caps *caps, u32 result) +{ + int rtn; + struct sl_ctrl_link *ctrl_link; + union sl_lgrp_notif_info info; + + ctrl_link = tag; + info.lp_link_caps = *caps; + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "an lp caps get callback"); + + switch (result) { + case SL_CORE_LINK_LP_CAPS_DATA: + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_AN_DATA, &info, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "lp caps data ctrl_lgrp_notif_enqueue failed [%d]", rtn); + break; + case SL_CORE_LINK_LP_CAPS_TIMEOUT: + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_AN_TIMEOUT, NULL, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "lp caps timeout ctrl_lgrp_notif_enqueue failed [%d]", rtn); + break; + case SL_CORE_LINK_LP_CAPS_ERROR: + default: + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_link->ctrl_lgrp, ctrl_link->num, + SL_LGRP_NOTIF_AN_ERROR, NULL, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "lp caps error ctrl_lgrp_notif_enqueue failed [%d]", rtn); + break; + } + + return 0; +} + +int sl_ctrl_link_down_callback(void *tag, u32 core_state, u64 core_cause_map, u64 core_info_map) +{ + int rtn; + struct sl_ctrl_link *ctrl_link; + char cause_str[100]; + + ctrl_link = tag; + + sl_link_down_cause_map_with_info_str(core_cause_map, cause_str, sizeof(cause_str)); + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "down callback (core_state = %u %s, core_cause = 0x%llX %s, core_info_map = %llu)", + core_state, sl_core_link_state_str(core_state), core_cause_map, cause_str, core_info_map); + + switch (core_state) { + case SL_CORE_LINK_STATE_DOWN: + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN); + + if (core_cause_map & SL_LINK_DOWN_CAUSE_CANCELED) + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_UP_CANCELED); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + complete_all(&ctrl_link->down_complete); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "down callback (down_complete = 0x%p)", &ctrl_link->down_complete); + + rtn = sl_ctrl_link_down_notif_send(ctrl_link, core_cause_map, core_info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "down callback ctrl_link_down_notif_send failed [%d]", rtn); + return 0; + default: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "down callback invalid (core_state = %u %s)", + core_state, sl_core_link_state_str(core_state)); + return -EBADRQC; + } +} + +static int sl_ctrl_link_async_down_callback(void *tag, u32 core_state, u64 core_cause_map, u64 core_info_map) +{ + int rtn; + struct sl_ctrl_link *ctrl_link; + char cause_str[100]; + + ctrl_link = tag; + + sl_link_down_cause_map_with_info_str(core_cause_map, cause_str, sizeof(cause_str)); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "async down callback (core_state = %u %s, core_cause = 0x%llX %s, core_info_map = %llu)", + core_state, sl_core_link_state_str(core_state), core_cause_map, cause_str, core_info_map); + + switch (core_state) { + case SL_CORE_LINK_STATE_DOWN: + SL_CTRL_LINK_COUNTER_INC(ctrl_link, LINK_DOWN); + + sl_ctrl_link_fec_mon_stop(ctrl_link); + cancel_work_sync(&ctrl_link->fec_mon_timer_work); + + flush_work(&ctrl_link->ctrl_lgrp->notif_work); + sl_ctrl_link_state_set(ctrl_link, SL_LINK_STATE_DOWN); + complete_all(&ctrl_link->down_complete); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "async down callback (down_complete = 0x%p)", &ctrl_link->down_complete); + + rtn = sl_ctrl_async_link_down_notif_send(ctrl_link, core_cause_map, core_info_map); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "async down callback async_link_down_notif_send failed [%d]", rtn); + + sl_ctrl_link_put(ctrl_link); + return 0; + default: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async down callback invalid (core_state = %u %s)", + core_state, sl_core_link_state_str(core_state)); + + sl_ctrl_link_put(ctrl_link); + return -EBADRQC; + } +} + +int sl_ctrl_link_async_down(struct sl_ctrl_link *ctrl_link, u64 down_cause_map, bool force_down) +{ + int rtn; + u32 link_state; + u32 policy_options; + bool accepted; + char cause_str[SL_LINK_DOWN_CAUSE_STR_SIZE]; + + if (!sl_ctrl_link_kref_get_unless_zero(ctrl_link)) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, + "async_down kref_get_unless_zero failed (ctrl_link = 0x%p)", ctrl_link); + return -EBADRQC; + } + + sl_link_down_cause_map_with_info_str(down_cause_map, cause_str, sizeof(cause_str)); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "async_down (down_cause_map = 0x%llX %s)", down_cause_map, cause_str); + + sl_ctrl_link_up_clock_reset(ctrl_link); + + policy_options = sl_ctrl_link_policy_options_get(ctrl_link); + + spin_lock(&ctrl_link->data_lock); + link_state = ctrl_link->state; + switch (link_state) { + case SL_LINK_STATE_UP_DOWN_REQ: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async_down already requested"); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_put(ctrl_link); + return 0; + case SL_LINK_STATE_UP: + if (!force_down && (policy_options & SL_LINK_POLICY_OPT_LINK_DOWN_REQ)) { + ctrl_link->state = SL_LINK_STATE_UP_DOWN_REQ; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async_down request link down"); + spin_unlock(&ctrl_link->data_lock); + rtn = sl_ctrl_link_down_req_notif_send(ctrl_link, down_cause_map, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_link, LOG_NAME, + "async_down ctrl_link_down_req_notif_send failed [%d]", + rtn); + sl_ctrl_link_put(ctrl_link); + return 0; + } + ctrl_link->state = SL_LINK_STATE_STOPPING; + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async_down stopping"); + spin_unlock(&ctrl_link->data_lock); + kref_get(&ctrl_link->ref_cnt); + rtn = sl_core_link_down_request(ctrl_link->ctrl_lgrp->ctrl_ldev->num, + ctrl_link->ctrl_lgrp->num, ctrl_link->num, + sl_ctrl_link_async_down_callback, ctrl_link, + down_cause_map, &accepted); + if (!accepted) + sl_ctrl_link_put(ctrl_link); + if (rtn) { + sl_ctrl_log_err(ctrl_link, LOG_NAME, "async_down core_link_down failed [%d]", rtn); + spin_lock(&ctrl_link->data_lock); + if (ctrl_link->state == SL_LINK_STATE_STOPPING) + ctrl_link->state = SL_LINK_STATE_UP; + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_put(ctrl_link); + return rtn; + } + sl_ctrl_link_put(ctrl_link); + return 0; + case SL_LINK_STATE_DOWN: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async_down already down"); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_put(ctrl_link); + return 0; + case SL_LINK_STATE_STOPPING: + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, "async_down already stopping"); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_put(ctrl_link); + return 0; + default: + sl_ctrl_log_err(ctrl_link, LOG_NAME, "async_down invalid state (link_state = %u %s)", + link_state, sl_link_state_str(link_state)); + spin_unlock(&ctrl_link->data_lock); + sl_ctrl_link_put(ctrl_link); + return -EBADRQC; + } +} + +void sl_ctrl_link_config_get(struct sl_ctrl_link *ctrl_link, struct sl_link_config *link_config) +{ + spin_lock(&ctrl_link->config_lock); + *link_config = ctrl_link->config; + spin_unlock(&ctrl_link->config_lock); +} + +void sl_ctrl_link_policy_get(struct sl_ctrl_link *ctrl_link, struct sl_link_policy *link_policy) +{ + spin_lock(&ctrl_link->config_lock); + *link_policy = ctrl_link->policy; + spin_unlock(&ctrl_link->config_lock); +} + +s32 sl_ctrl_link_policy_fec_mon_ucw_down_limit_get(struct sl_ctrl_link *ctrl_link) +{ + s32 fec_mon_ucw_down_limit; + + spin_lock(&ctrl_link->config_lock); + fec_mon_ucw_down_limit = ctrl_link->policy.fec_mon_ucw_down_limit; + spin_unlock(&ctrl_link->config_lock); + + return fec_mon_ucw_down_limit; +} + +s32 sl_ctrl_link_policy_fec_mon_ucw_warn_limit_get(struct sl_ctrl_link *ctrl_link) +{ + s32 fec_mon_ucw_warn_limit; + + spin_lock(&ctrl_link->config_lock); + fec_mon_ucw_warn_limit = ctrl_link->policy.fec_mon_ucw_warn_limit; + spin_unlock(&ctrl_link->config_lock); + + return fec_mon_ucw_warn_limit; +} + +s32 sl_ctrl_link_policy_fec_mon_ccw_down_limit_get(struct sl_ctrl_link *ctrl_link) +{ + s32 fec_mon_ccw_down_limit; + + spin_lock(&ctrl_link->config_lock); + fec_mon_ccw_down_limit = ctrl_link->policy.fec_mon_ccw_down_limit; + spin_unlock(&ctrl_link->config_lock); + + return fec_mon_ccw_down_limit; +} + +s32 sl_ctrl_link_policy_fec_mon_ccw_warn_limit_get(struct sl_ctrl_link *ctrl_link) +{ + s32 fec_mon_ccw_warn_limit; + + spin_lock(&ctrl_link->config_lock); + fec_mon_ccw_warn_limit = ctrl_link->policy.fec_mon_ccw_warn_limit; + spin_unlock(&ctrl_link->config_lock); + + return fec_mon_ccw_warn_limit; +} + +s32 sl_ctrl_link_policy_fec_mon_period_ms_get(struct sl_ctrl_link *ctrl_link) +{ + s32 fec_mon_period_ms; + + spin_lock(&ctrl_link->config_lock); + fec_mon_period_ms = ctrl_link->policy.fec_mon_period_ms; + spin_unlock(&ctrl_link->config_lock); + + return fec_mon_period_ms; +} + +u32 sl_ctrl_link_policy_options_get(struct sl_ctrl_link *ctrl_link) +{ + u32 options; + + spin_lock(&ctrl_link->config_lock); + options = ctrl_link->policy.options; + spin_unlock(&ctrl_link->config_lock); + + return options; +} + +void sl_ctrl_link_state_set(struct sl_ctrl_link *ctrl_link, u32 link_state) +{ + spin_lock(&ctrl_link->data_lock); + ctrl_link->state = link_state; + spin_unlock(&ctrl_link->data_lock); +} + +u32 sl_ctrl_link_state_get(struct sl_ctrl_link *ctrl_link) +{ + u32 state; + + spin_lock(&ctrl_link->data_lock); + state = ctrl_link->state; + spin_unlock(&ctrl_link->data_lock); + + return state; +} + +int sl_ctrl_link_last_up_fail_cause_map_get(struct sl_ctrl_link *ctrl_link, u64 *last_up_fail_cause_map) +{ + spin_lock(&ctrl_link->data_lock); + *last_up_fail_cause_map = ctrl_link->last_up_fail_cause_map; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "last_up_fail_cause_map_get (last_up_fail_cause_map = 0x%llX)", *last_up_fail_cause_map); + + return 0; +} + +int sl_ctrl_link_last_up_fail_cause_info_get(struct sl_ctrl_link *ctrl_link, u64 *last_up_fail_cause_map, + time64_t *last_up_fail_time) +{ + spin_lock(&ctrl_link->data_lock); + *last_up_fail_cause_map = ctrl_link->last_up_fail_cause_map; + *last_up_fail_time = ctrl_link->last_up_fail_time; + spin_unlock(&ctrl_link->data_lock); + + sl_ctrl_log_dbg(ctrl_link, LOG_NAME, + "last up fail time show (cause_map = 0x%llX, time = %lld %ptTt %ptTd)", + *last_up_fail_cause_map, *last_up_fail_time, last_up_fail_time, last_up_fail_time); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr.c new file mode 100644 index 000000000000..016ea353da4f --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include + +#include "sl_asic.h" +#include "sl_sysfs.h" +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_lgrp_notif.h" +#include "sl_ctrl_llr.h" +#include "data/sl_core_data_llr.h" +#include "sl_core_str.h" +#include "sl_ctrl_llr_counters.h" + +#define LOG_NAME SL_CTRL_LLR_LOG_NAME + +#define SL_CTRL_LLR_DEL_WAIT_TIMEOUT_MS 2000 + +static struct sl_ctrl_llr *ctrl_llrs[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS]; +static DEFINE_SPINLOCK(ctrl_llrs_lock); + +static void sl_ctrl_llr_setup_callback(void *tag, + enum sl_core_llr_state core_llr_state, + u64 core_imap, + struct sl_llr_data core_llr_data) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + union sl_lgrp_notif_info info; + + ctrl_llr = tag; + + ctrl_llr->setup.state = core_llr_state; + ctrl_llr->setup.imap = core_imap; + ctrl_llr->setup.data = core_llr_data; + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "setup callback (state = %u %s)", core_llr_state, + sl_core_llr_state_str(core_llr_state)); + + switch (core_llr_state) { + case SL_CORE_LLR_STATE_SETUP: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_SETUP); + info.llr_data = ctrl_llr->setup.data; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_SETUP, &info, ctrl_llr->setup.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "setup SETUP ctrl_lgrp_notif_enqueue failed [%d]", rtn); + return; + case SL_CORE_LLR_STATE_SETUP_TIMEOUT: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_SETUP_TIMEOUT); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_SETUP_TIMEOUT, NULL, ctrl_llr->setup.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "setup TIMEOUT ctrl_lgrp_notif_enqueue failed [%d]", rtn); + return; + case SL_CORE_LLR_STATE_CONFIGURED: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_CONFIGURED); + memset(&ctrl_llr->setup.data, 0, sizeof(struct sl_llr_data)); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_CANCELED, NULL, ctrl_llr->setup.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "setup CONFIGURED ctrl_lgrp_notif_enqueue failed [%d]", rtn); + return; + default: + memset(&ctrl_llr->setup.data, 0, sizeof(struct sl_llr_data)); + sl_ctrl_log_err(ctrl_llr, LOG_NAME, + "setup ERROR invalid (state = %u %s)", + core_llr_state, sl_core_llr_state_str(core_llr_state)); + info.error = -ENOMSG; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_ERROR, &info, ctrl_llr->setup.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "setup ERROR ctrl_lgrp_notif_enqueue failed [%d]", rtn); + return; + } +} + +static void sl_ctrl_llr_start_callback(void *tag, + enum sl_core_llr_state core_llr_state, + u64 core_imap) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + union sl_lgrp_notif_info info; + + ctrl_llr = tag; + + ctrl_llr->start.state = core_llr_state; + ctrl_llr->start.imap = core_imap; + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "start callback (state = %u %s)", + core_llr_state, sl_core_llr_state_str(core_llr_state)); + + switch (core_llr_state) { + case SL_CORE_LLR_STATE_RUNNING: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_RUNNING); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_RUNNING, NULL, ctrl_llr->start.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "start RUNNING ctrl_lgrp_notif_enqueue failed [%d[", rtn); + return; + case SL_CORE_LLR_STATE_START_TIMEOUT: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_START_TIMEOUT); + if (ctrl_llr->policy.options & SL_LLR_POLICY_OPT_CONTINUOUS_START_TRIES) { + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "start TIMEOUT retry"); + rtn = sl_core_llr_start(ctrl_llr->ctrl_lgrp->ctrl_ldev->num, ctrl_llr->ctrl_lgrp->num, + ctrl_llr->num, sl_ctrl_llr_start_callback, ctrl_llr, 0); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "start TIMEOUT core_llr_start retry failed [%d]", rtn); + } + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_START_TIMEOUT, NULL, ctrl_llr->start.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "start TIMEOUT ctrl_lgrp_notif_enqueue failed [%d[", rtn); + return; + case SL_CORE_LLR_STATE_SETUP: + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_SETUP); + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, + SL_LGRP_NOTIF_LLR_CANCELED, NULL, ctrl_llr->start.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "start CONFIGURED ctrl_lgrp_notif_enqueue failed [%d[", rtn); + return; + default: + memset(&ctrl_llr->setup.data, 0, sizeof(struct sl_llr_data)); + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "start ERROR invalid (state = %u %s)", + core_llr_state, sl_core_llr_state_str(core_llr_state)); + info.error = -ENOMSG; + rtn = sl_ctrl_lgrp_notif_enqueue(ctrl_llr->ctrl_lgrp, ctrl_llr->num, SL_LGRP_NOTIF_LLR_ERROR, + &info, ctrl_llr->setup.imap); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, "start ERROR ctrl_lgrp_notif_enqueue failed [%d[", + rtn); + return; + } +} + +int sl_ctrl_llr_new(u8 ldev_num, u8 lgrp_num, u8 llr_num, struct kobject *sysfs_parent) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, "exists (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + ctrl_llr = kzalloc(sizeof(*ctrl_llr), GFP_KERNEL); + if (!ctrl_llr) + return -ENOMEM; + + ctrl_llr->magic = SL_CTRL_LLR_MAGIC; + ctrl_llr->ver = SL_CTRL_LLR_VER; + ctrl_llr->num = llr_num; + ctrl_llr->ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_llr->ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, + "new NULL lgrp (ldev_num = %u, lgrp_num = %u)", ldev_num, lgrp_num); + kfree(ctrl_llr); + return -EBADRQC; + } + + spin_lock_init(&ctrl_llr->data_lock); + kref_init(&ctrl_llr->ref_cnt); + init_completion(&ctrl_llr->del_complete); + + rtn = sl_ctrl_llr_counters_init(ctrl_llr); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "ctrl_llr_counters_init failed [%d]", rtn); + goto out; + } + + rtn = sl_ctrl_llr_cause_counters_init(ctrl_llr); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "ctrl_llr_cause_counters_init failed [%d]", rtn); + goto out_counters; + } + + rtn = sl_core_llr_new(ldev_num, lgrp_num, llr_num); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "core_llr_new failed [%d]", rtn); + goto out_cause_counters; + } + + if (sysfs_parent) { + ctrl_llr->parent_kobj = sysfs_parent; + + rtn = sl_sysfs_llr_create(ctrl_llr); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "sysfs_llr_create failed [%d]", rtn); + goto out_llr_new; + } + } + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "new (ctrl_llr = 0x%p)", ctrl_llr); + + spin_lock(&ctrl_llrs_lock); + ctrl_llrs[ldev_num][lgrp_num][llr_num] = ctrl_llr; + spin_unlock(&ctrl_llrs_lock); + + return 0; +out_llr_new: + sl_core_llr_del(ldev_num, lgrp_num, llr_num); +out_cause_counters: + sl_ctrl_llr_cause_counters_del(ctrl_llr); +out_counters: + sl_ctrl_llr_counters_del(ctrl_llr); +out: + kfree(ctrl_llr); + return rtn; +} + +static void sl_ctrl_llr_release(struct kref *kref) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + u8 ldev_num; + u8 lgrp_num; + u8 llr_num; + + ctrl_llr = container_of(kref, struct sl_ctrl_llr, ref_cnt); + ldev_num = ctrl_llr->ctrl_lgrp->ctrl_ldev->num; + lgrp_num = ctrl_llr->ctrl_lgrp->num; + llr_num = ctrl_llr->num; + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "release (ctrl_llr = 0x%p)", ctrl_llr); + + rtn = sl_core_llr_stop(ldev_num, lgrp_num, llr_num); + if (rtn) + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, "release core_llr_stop failed [%d]", rtn); + + /* Must delete sysfs first to guarantee nobody is reading */ + sl_sysfs_llr_delete(ctrl_llr); + + sl_core_llr_del(ldev_num, lgrp_num, llr_num); + + /* Free the counters and unpublish together so cause_counter_inc() sees neither half-done */ + spin_lock(&ctrl_llrs_lock); + sl_ctrl_llr_counters_del(ctrl_llr); + sl_ctrl_llr_cause_counters_del(ctrl_llr); + ctrl_llrs[ldev_num][lgrp_num][llr_num] = NULL; + spin_unlock(&ctrl_llrs_lock); + + complete_all(&ctrl_llr->del_complete); + + kfree(ctrl_llr); +} + +static int sl_ctrl_llr_put(struct sl_ctrl_llr *ctrl_llr) +{ + return kref_put(&ctrl_llr->ref_cnt, sl_ctrl_llr_release); +} + +int sl_ctrl_llr_del(u8 ldev_num, u8 lgrp_num, u8 llr_num) +{ + struct sl_ctrl_llr *ctrl_llr; + unsigned long timeleft; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "del not found (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + /* Release occurs on the last caller. Block until complete. */ + if (!sl_ctrl_llr_put(ctrl_llr)) { + timeleft = wait_for_completion_timeout(&ctrl_llr->del_complete, + msecs_to_jiffies(SL_CTRL_LLR_DEL_WAIT_TIMEOUT_MS)); + + if (timeleft == 0) { + sl_ctrl_log_err(NULL, LOG_NAME, + "del completion_timeout (ctrl_llr = 0x%p)", ctrl_llr); + return -ETIMEDOUT; + } + } + + return 0; +} + +static bool sl_ctrl_llr_kref_get_unless_zero(struct sl_ctrl_llr *ctrl_llr) +{ + return (kref_get_unless_zero(&ctrl_llr->ref_cnt) != 0); +} + +struct sl_ctrl_llr *sl_ctrl_llr_get(u8 ldev_num, u8 lgrp_num, u8 llr_num) +{ + struct sl_ctrl_llr *ctrl_llr; + + spin_lock(&ctrl_llrs_lock); + ctrl_llr = ctrl_llrs[ldev_num][lgrp_num][llr_num]; + spin_unlock(&ctrl_llrs_lock); + + sl_ctrl_log_dbg(NULL, LOG_NAME, + "get (ldev_num = %u, lgrp_num = %u, llr_num = %u, ctrl_llr = 0x%p)", + ldev_num, lgrp_num, llr_num, ctrl_llr); + + return ctrl_llr; +} + +/* + * The lookup and the counter update are done under ctrl_llrs_lock, which + * sl_ctrl_llr_release() also holds while it frees the counters. + */ +void sl_ctrl_llr_cause_counter_inc(struct sl_core_llr *core_llr, unsigned long cause_map) +{ + struct sl_ctrl_llr *ctrl_llr; + u8 ldev_num = core_llr->core_lgrp->core_ldev->num; + u8 lgrp_num = core_llr->core_lgrp->num; + + spin_lock(&ctrl_llrs_lock); + ctrl_llr = ctrl_llrs[ldev_num][lgrp_num][core_llr->num]; + if (ctrl_llr) + sl_ctrl_llr_cause_counters_inc(ctrl_llr, cause_map); + spin_unlock(&ctrl_llrs_lock); + + if (!ctrl_llr) + sl_ctrl_log_dbg(NULL, LOG_NAME, "llr cause counter inc - llr not found"); +} + +int sl_ctrl_llr_config_set(u8 ldev_num, u8 lgrp_num, u8 llr_num, struct sl_llr_config *llr_config) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "config set NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "config set kref_get_unless_zero failed (ctrl_llr = 0x%p)", + ctrl_llr); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "config set (setup timeout = %d, start timeout = %d)", + llr_config->setup_timeout_ms, llr_config->start_timeout_ms); + + rtn = sl_core_llr_config_set(ldev_num, lgrp_num, llr_num, llr_config); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "core_llr_config_set failed [%d]", rtn); + goto out; + } + + spin_lock(&ctrl_llr->data_lock); + ctrl_llr->config = *llr_config; + spin_unlock(&ctrl_llr->data_lock); + +out: + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "config set - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +int sl_ctrl_llr_policy_set(u8 ldev_num, u8 lgrp_num, u8 llr_num, struct sl_llr_policy *llr_policy) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "policy set NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "policy set kref_get_unless_zero failed (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "policy set"); + + rtn = sl_core_llr_policy_set(ldev_num, lgrp_num, llr_num, llr_policy); + if (rtn) { + sl_ctrl_log_err(ctrl_llr, LOG_NAME, "core_llr_policy_set failed [%d]", rtn); + goto out; + } + + spin_lock(&ctrl_llr->data_lock); + ctrl_llr->policy = *llr_policy; + spin_unlock(&ctrl_llr->data_lock); + +out: + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "policy set - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +int sl_ctrl_llr_setup(u8 ldev_num, u8 lgrp_num, u8 llr_num) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "setup NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "setup kref_get_unless_zero failed (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "setup"); + + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_SETUP_CMD); + + rtn = sl_core_llr_setup(ldev_num, lgrp_num, llr_num, + sl_ctrl_llr_setup_callback, ctrl_llr, 0); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_llr, LOG_NAME, "core_llr_setup failed [%d]", rtn); + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_SETUP_FAIL); + } + + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, "setup - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +int sl_ctrl_llr_start(u8 ldev_num, u8 lgrp_num, u8 llr_num) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "start NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "start kref_get_unless_zero failed (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "start"); + + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_START_CMD); + + rtn = sl_core_llr_start(ldev_num, lgrp_num, llr_num, + sl_ctrl_llr_start_callback, ctrl_llr, 0); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_llr, LOG_NAME, "core_llr_start failed [%d]", rtn); + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_START_FAIL); + } + + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, "start - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +int sl_ctrl_llr_stop(u8 ldev_num, u8 lgrp_num, u8 llr_num) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "stop NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "stop kref_get_unless_zero failed (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "stop"); + + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_STOP_CMD); + + rtn = sl_core_llr_stop(ldev_num, lgrp_num, llr_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_llr, LOG_NAME, "core_llr_stop failed [%d]", rtn); + SL_CTRL_LLR_COUNTER_INC(ctrl_llr, LLR_STOP_FAIL); + } + + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, "stop - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +enum sl_llr_state sl_ctrl_llr_state_from_core_llr_state(enum sl_core_llr_state core_llr_state) +{ + switch (core_llr_state) { + case SL_CORE_LLR_STATE_NEW: + return SL_LLR_STATE_OFF; + case SL_CORE_LLR_STATE_CONFIGURED: + case SL_CORE_LLR_STATE_SETUP_TIMEOUT: + case SL_CORE_LLR_STATE_SETUP_STOPPING: + return SL_LLR_STATE_CONFIGURED; + case SL_CORE_LLR_STATE_SETTING_UP: + return SL_LLR_STATE_SETUP_BUSY; + case SL_CORE_LLR_STATE_SETUP: + case SL_CORE_LLR_STATE_START_TIMEOUT: + return SL_LLR_STATE_SETUP; + case SL_CORE_LLR_STATE_STARTING: + return SL_LLR_STATE_START_BUSY; + case SL_CORE_LLR_STATE_RUNNING: + return SL_LLR_STATE_RUNNING; + case SL_CORE_LLR_STATE_STOPPING: + return SL_LLR_STATE_STOP_BUSY; + case SL_CORE_LLR_STATE_SETUP_CANCELING: + case SL_CORE_LLR_STATE_START_CANCELING: + return SL_LLR_STATE_CANCELING; + case SL_CORE_LLR_STATE_INVALID: + default: + return SL_LLR_STATE_INVALID; + } +} + +int sl_ctrl_llr_state_get(u8 ldev_num, u8 lgrp_num, u8 llr_num, enum sl_llr_state *state) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + enum sl_core_llr_state core_llr_state; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "state get NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "state get kref_get_unless_zero failed (ctrl_llr = 0x%p)", + ctrl_llr); + return -EBADRQC; + } + + rtn = sl_core_llr_state_get(ldev_num, lgrp_num, llr_num, &core_llr_state); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_llr, LOG_NAME, "core_llr_state_get failed [%d]", rtn); + *state = SL_LLR_STATE_OFF; + rtn = 0; + goto out; + } + + *state = sl_ctrl_llr_state_from_core_llr_state(core_llr_state); + if (*state == SL_LLR_STATE_INVALID) + sl_ctrl_log_err(ctrl_llr, LOG_NAME, + "invalid (core_llr_state = %u %s)", + core_llr_state, sl_core_llr_state_str(core_llr_state)); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "state (core = %u %s, ctrl = %u %s)", + core_llr_state, sl_core_llr_state_str(core_llr_state), + *state, sl_llr_state_str(*state)); + +out: + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "state get - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} + +int sl_ctrl_llr_info_map_get(u8 ldev_num, u8 lgrp_num, u8 llr_num, u64 *info_map) +{ + int rtn; + struct sl_ctrl_llr *ctrl_llr; + + ctrl_llr = sl_ctrl_llr_get(ldev_num, lgrp_num, llr_num); + if (!ctrl_llr) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info map get NULL llr (ldev_num = %u, lgrp_num = %u, llr_num = %u)", + ldev_num, lgrp_num, llr_num); + return -EBADRQC; + } + + if (!sl_ctrl_llr_kref_get_unless_zero(ctrl_llr)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info map get kref_get_unless_zero failed (ctrl_llr = 0x%p)", ctrl_llr); + return -EBADRQC; + } + + rtn = sl_core_data_llr_info_map_get(sl_core_llr_get(ldev_num, lgrp_num, llr_num), info_map); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_llr, LOG_NAME, "core_data_llr_info_map_get failed [%d]", rtn); + goto out; + } + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "info map get (info_map = 0x%llX)", *info_map); + +out: + if (sl_ctrl_llr_put(ctrl_llr)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "info map get - llr removed (ctrl_llr = 0x%p)", ctrl_llr); + + return rtn; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr_counters.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr_counters.c new file mode 100644 index 000000000000..81af580d3837 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_llr_counters.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include + +#include "sl_ctrl_llr.h" +#include "sl_ctrl_llr_counters.h" +#include "base/sl_ctrl_log.h" + +#define LOG_NAME SL_CTRL_LLR_LOG_NAME + +#define SL_CTRL_LLR_COUNTER_INIT(_llr, _counter) \ + (_llr)->counters[_counter].name = #_counter + +#define SL_CTRL_LLR_CAUSE_COUNTER_INIT(_llr, _counter) \ + (_llr)->cause_counters[_counter].name = #_counter + +#define SL_CTRL_LLR_CAUSE_COUNTER_INC(_llr, _counter) \ + atomic_inc(&(_llr)->cause_counters[_counter].count) + +int sl_ctrl_llr_counters_init(struct sl_ctrl_llr *ctrl_llr) +{ + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "llr counters init"); + + ctrl_llr->counters = kzalloc(sizeof(*ctrl_llr->counters) * SL_CTRL_LLR_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_llr->counters) + return -ENOMEM; + + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_SETUP_CMD); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_SETUP); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_SETUP_TIMEOUT); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_SETUP_FAIL); + + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_CONFIGURED); + + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_START_CMD); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_RUNNING); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_START_TIMEOUT); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_START_FAIL); + + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_STOP_CMD); + SL_CTRL_LLR_COUNTER_INIT(ctrl_llr, LLR_STOP_FAIL); + + return 0; +} + +int sl_ctrl_llr_cause_counters_init(struct sl_ctrl_llr *ctrl_llr) +{ + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "llr cause counters init"); + + ctrl_llr->cause_counters = kzalloc(sizeof(*ctrl_llr->cause_counters) * + SL_CTRL_LLR_CAUSE_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_llr->cause_counters) + return -ENOMEM; + + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_SETUP_CONFIG); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_SETUP_INTR_ENABLE); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_SETUP_TIMEOUT); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_START_INTR_ENABLE); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_START_TIMEOUT); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_COMMAND); + SL_CTRL_LLR_CAUSE_COUNTER_INIT(ctrl_llr, LLR_CAUSE_CANCELED); + + return 0; +} + +int sl_ctrl_llr_counter_get(struct sl_ctrl_llr *ctrl_llr, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_llr->counters[counter].count); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (counter = %u %s, count = %d)", counter, + ctrl_llr->counters[counter].name, *count); + + return 0; +} + +int sl_ctrl_llr_cause_counter_get(struct sl_ctrl_llr *ctrl_llr, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_llr->cause_counters[counter].count); + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "get (cause counter = %u %s, count = %d)", counter, + ctrl_llr->cause_counters[counter].name, *count); + + return 0; +} + +void sl_ctrl_llr_counters_del(struct sl_ctrl_llr *ctrl_llr) +{ + kfree(ctrl_llr->counters); +} + +void sl_ctrl_llr_cause_counters_del(struct sl_ctrl_llr *ctrl_llr) +{ + kfree(ctrl_llr->cause_counters); +} + +void sl_ctrl_llr_cause_counters_inc(struct sl_ctrl_llr *ctrl_llr, unsigned long cause_map) +{ + unsigned long which; + + sl_ctrl_log_dbg(ctrl_llr, LOG_NAME, "llr cause counter inc"); + + BUILD_BUG_ON(SL_CTRL_LLR_CAUSE_COUNTERS_COUNT > 32); + + for_each_set_bit(which, &cause_map, sizeof(cause_map) * BITS_PER_BYTE) { + switch (BIT(which)) { + case SL_LLR_FAIL_CAUSE_SETUP_CONFIG: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_SETUP_CONFIG); + break; + case SL_LLR_FAIL_CAUSE_SETUP_INTR_ENABLE: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_SETUP_INTR_ENABLE); + break; + case SL_LLR_FAIL_CAUSE_SETUP_TIMEOUT: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_SETUP_TIMEOUT); + break; + case SL_LLR_FAIL_CAUSE_START_INTR_ENABLE: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_START_INTR_ENABLE); + break; + case SL_LLR_FAIL_CAUSE_START_TIMEOUT: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_START_TIMEOUT); + break; + case SL_LLR_FAIL_CAUSE_COMMAND: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_COMMAND); + break; + case SL_LLR_FAIL_CAUSE_CANCELED: + SL_CTRL_LLR_CAUSE_COUNTER_INC(ctrl_llr, LLR_CAUSE_CANCELED); + break; + default: + sl_ctrl_log_warn_trace(ctrl_llr, LOG_NAME, + "cause_counter_inc unknown cause (bit = %lu)", which); + break; + } + } +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac.c new file mode 100644 index 000000000000..a4202757d238 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2023,2024,2025 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include +#include + +#include + +#include "sl_asic.h" +#include "sl_sysfs.h" +#include "base/sl_ctrl_log.h" +#include "sl_ctrl_ldev.h" +#include "sl_ctrl_lgrp.h" +#include "sl_ctrl_mac.h" +#include "sl_core_mac.h" +#include "sl_ctrl_mac_counters.h" + +#define SL_CTRL_MAC_DEL_WAIT_TIMEOUT_MS 2000 + +#define LOG_NAME SL_CTRL_MAC_LOG_NAME + +static struct sl_ctrl_mac *ctrl_macs[SL_ASIC_MAX_LDEVS][SL_ASIC_MAX_LGRPS][SL_ASIC_MAX_LINKS]; +static DEFINE_SPINLOCK(ctrl_macs_lock); + +int sl_ctrl_mac_new(u8 ldev_num, u8 lgrp_num, u8 mac_num, struct kobject *sysfs_parent) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (ctrl_mac) { + sl_ctrl_log_err(NULL, LOG_NAME, "exists (mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + ctrl_mac = kzalloc(sizeof(*ctrl_mac), GFP_KERNEL); + if (!ctrl_mac) + return -ENOMEM; + + ctrl_mac->magic = SL_CTRL_MAC_MAGIC; + ctrl_mac->ver = SL_CTRL_MAC_VER; + ctrl_mac->num = mac_num; + ctrl_mac->ctrl_lgrp = sl_ctrl_lgrp_get(ldev_num, lgrp_num); + if (!ctrl_mac->ctrl_lgrp) { + sl_ctrl_log_err(NULL, LOG_NAME, + "new NULL lgrp (ldev_num = %u, lgrp_num = %u)", ldev_num, lgrp_num); + kfree(ctrl_mac); + return -EBADRQC; + } + + kref_init(&ctrl_mac->ref_cnt); + kref_init(&ctrl_mac->mem_ref); + init_completion(&ctrl_mac->del_complete); + + spin_lock_init(&ctrl_mac->data_lock); + + rtn = sl_ctrl_mac_counters_init(ctrl_mac); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "ctrl_mac_counters_init failed [%d]", rtn); + goto out; + } + + rtn = sl_core_mac_new(ldev_num, lgrp_num, mac_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "core_mac_new failed [%d]", rtn); + goto out_counters; + } + + if (sysfs_parent) { + ctrl_mac->parent_kobj = sysfs_parent; + + rtn = sl_sysfs_mac_create(ctrl_mac); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "sysfs_mac_create failed [%d]", rtn); + goto out_mac_new; + } + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "new (mac = 0x%p)", ctrl_mac); + + spin_lock(&ctrl_macs_lock); + ctrl_macs[ldev_num][lgrp_num][mac_num] = ctrl_mac; + spin_unlock(&ctrl_macs_lock); + + return 0; + +out_mac_new: + sl_core_mac_del(ldev_num, lgrp_num, mac_num); +out_counters: + sl_ctrl_mac_counters_del(ctrl_mac); +out: + kfree(ctrl_mac); + return rtn; +} + +static void sl_ctrl_mac_free(struct kref *kref) +{ + kfree(container_of(kref, struct sl_ctrl_mac, mem_ref)); +} + +static void sl_ctrl_mac_release(struct kref *kref) +{ + struct sl_ctrl_mac *ctrl_mac; + u8 ldev_num; + u8 lgrp_num; + u8 mac_num; + + ctrl_mac = container_of(kref, struct sl_ctrl_mac, ref_cnt); + ldev_num = ctrl_mac->ctrl_lgrp->ctrl_ldev->num; + lgrp_num = ctrl_mac->ctrl_lgrp->num; + mac_num = ctrl_mac->num; + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "release (ctrl_mac = 0x%p)", ctrl_mac); + + /* Must delete sysfs first to guarantee nobody is reading */ + sl_sysfs_mac_delete(ctrl_mac); + + sl_core_mac_del(ldev_num, lgrp_num, mac_num); + + sl_ctrl_mac_counters_del(ctrl_mac); + + spin_lock(&ctrl_macs_lock); + ctrl_macs[ldev_num][lgrp_num][mac_num] = NULL; + spin_unlock(&ctrl_macs_lock); + + /* + * Wake a waiting sl_ctrl_mac_del(). The memory is not freed here: + * the waiter still uses del_complete and ctrl_mac after it wakes, + * and holds mem_ref until it is done. The last holder frees it. + */ + complete_all(&ctrl_mac->del_complete); + kref_put(&ctrl_mac->mem_ref, sl_ctrl_mac_free); +} + +static int sl_ctrl_mac_put(struct sl_ctrl_mac *ctrl_mac) +{ + return kref_put(&ctrl_mac->ref_cnt, sl_ctrl_mac_release); +} + +int sl_ctrl_mac_del(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + struct sl_ctrl_mac *ctrl_mac; + unsigned long timeleft; + int rtn = 0; + + sl_ctrl_log_dbg(NULL, LOG_NAME, "del (ldev_num = %u, lgrp_num = %u, mac_num = %u)", + ldev_num, lgrp_num, mac_num); + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_err_trace(NULL, LOG_NAME, + "mac not found (ldev_num = %u, lgrp_num = %u, mac_num = %u)", + ldev_num, lgrp_num, mac_num); + return -EBADRQC; + } + + /* + * Keep the memory, and with it del_complete, valid until we are done + * waiting: the release may run on another thread and must not free it + * under us. + */ + kref_get(&ctrl_mac->mem_ref); + + /* Release occurs on the last caller. Block until complete. */ + if (!sl_ctrl_mac_put(ctrl_mac)) { + timeleft = wait_for_completion_timeout(&ctrl_mac->del_complete, + msecs_to_jiffies(SL_CTRL_MAC_DEL_WAIT_TIMEOUT_MS)); + + sl_ctrl_log_dbg(NULL, LOG_NAME, + "del completion_timeout (timeleft = %lums)", timeleft); + + if (timeleft == 0) { + sl_ctrl_log_err(NULL, LOG_NAME, + "del completion_timeout (ctrl_mac = 0x%p)", ctrl_mac); + rtn = -ETIMEDOUT; + } + } + + kref_put(&ctrl_mac->mem_ref, sl_ctrl_mac_free); + + return rtn; +} + +static bool sl_ctrl_mac_kref_get_unless_zero(struct sl_ctrl_mac *ctrl_mac) +{ + return (kref_get_unless_zero(&ctrl_mac->ref_cnt) != 0); +} + +struct sl_ctrl_mac *sl_ctrl_mac_get(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + struct sl_ctrl_mac *ctrl_mac; + + spin_lock(&ctrl_macs_lock); + ctrl_mac = ctrl_macs[ldev_num][lgrp_num][mac_num]; + spin_unlock(&ctrl_macs_lock); + + sl_ctrl_log_dbg(NULL, LOG_NAME, + "get (ldev_num = %u, lgrp_num = %u, mac_num = %u, mac = 0x%p)", + ldev_num, lgrp_num, mac_num, ctrl_mac); + + return ctrl_mac; +} + +int sl_ctrl_mac_tx_start(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_err(NULL, LOG_NAME, "tx start - NULL mac"); + return -EBADRQC; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "tx start - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "tx start"); + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_START_CMD); + + rtn = sl_core_mac_tx_start(ldev_num, lgrp_num, mac_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "tx start - core_mac_tx_start failed [%d]", rtn); + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_START_FAIL); + goto out; + } + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_STARTED); + + rtn = 0; + +out: + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "tx start - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return rtn; +} + +int sl_ctrl_mac_tx_stop(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_dbg(NULL, LOG_NAME, "tx stop - NULL mac"); + return 0; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "tx stop - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "tx stop"); + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_STOP_CMD); + + rtn = sl_core_mac_tx_stop(ldev_num, lgrp_num, mac_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "tx stop - core_mac_tx_stop failed [%d]", rtn); + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_STOP_FAIL); + goto out; + } + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_TX_STOPPED); + + rtn = 0; + +out: + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "tx stop - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return rtn; +} + +int sl_ctrl_mac_tx_state_get(u8 ldev_num, u8 lgrp_num, u8 mac_num, u32 *state) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + u32 core_mac_state; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_dbg(NULL, LOG_NAME, "tx state get - NULL mac"); + *state = SL_MAC_STATE_OFF; + return 0; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_dbg(NULL, LOG_NAME, + "tx state get - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + *state = SL_MAC_STATE_OFF; + return 0; + } + + rtn = sl_core_mac_tx_state_get(ldev_num, lgrp_num, mac_num, &core_mac_state); + if (rtn) { + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "tx state get - core_mac_tx_state_get failed [%d]", rtn); + *state = SL_MAC_STATE_OFF; + goto out; + } + + switch (core_mac_state) { + case SL_CORE_MAC_STATE_ON: + *state = SL_MAC_STATE_ON; + break; + default: + *state = SL_MAC_STATE_OFF; + break; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "tx state get (state = %u %s, core_state = %u)", + *state, sl_mac_state_str(*state), core_mac_state); + +out: + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "tx state get - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return 0; +} + +int sl_ctrl_mac_rx_start(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_err(NULL, LOG_NAME, "rx start - NULL mac"); + return -EBADRQC; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "rx start - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "rx start"); + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_START_CMD); + + rtn = sl_core_mac_rx_start(ldev_num, lgrp_num, mac_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "rx start - core_mac_rx_start failed [%d]", rtn); + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_START_FAIL); + goto out; + } + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_STARTED); + + rtn = 0; + +out: + + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "rx start - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return rtn; +} + +int sl_ctrl_mac_rx_stop(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_dbg(NULL, LOG_NAME, "rx stop - NULL mac"); + return 0; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "rx stop - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "rx stop"); + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_STOP_CMD); + + rtn = sl_core_mac_rx_stop(ldev_num, lgrp_num, mac_num); + if (rtn) { + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "rx stop - core_mac_rx_stop failed [%d]", rtn); + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_STOP_FAIL); + goto out; + } + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RX_STOPPED); + + rtn = 0; + +out: + + sl_ctrl_mac_put(ctrl_mac); + + return rtn; +} + +int sl_ctrl_mac_rx_state_get(u8 ldev_num, u8 lgrp_num, u8 mac_num, u32 *state) +{ + int rtn; + struct sl_ctrl_mac *ctrl_mac; + u32 core_mac_state; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_dbg(NULL, LOG_NAME, "rx state get - NULL mac"); + *state = SL_MAC_STATE_OFF; + return 0; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_dbg(NULL, LOG_NAME, + "rx state get - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + *state = SL_MAC_STATE_OFF; + return 0; + } + + rtn = sl_core_mac_rx_state_get(ldev_num, lgrp_num, mac_num, &core_mac_state); + if (rtn) { + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "rx state get - core_mac_rx_state_get failed [%d]", rtn); + *state = SL_MAC_STATE_OFF; + goto out; + } + + switch (core_mac_state) { + case SL_CORE_MAC_STATE_ON: + *state = SL_MAC_STATE_ON; + break; + default: + *state = SL_MAC_STATE_OFF; + break; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "rx state get (state = %u %s, core_state = %u)", + *state, sl_mac_state_str(*state), core_mac_state); + +out: + + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "rx state get - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return 0; +} + +int sl_ctrl_mac_reset(u8 ldev_num, u8 lgrp_num, u8 mac_num) +{ + int tx_rtn; + int rx_rtn; + int rtn = 0; + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_dbg(NULL, LOG_NAME, "reset - NULL mac"); + return 0; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "reset - kref unavailable (ctrl_mac = 0x%p)", ctrl_mac); + return -EBADRQC; + } + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "reset"); + + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RESET_CMD); + + tx_rtn = sl_core_mac_tx_stop(ldev_num, lgrp_num, mac_num); + if (tx_rtn) { + /* Log error, but allow the function to continue to mac rx stop */ + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "reset - core_mac_tx_stop failed [%d]", tx_rtn); + } + + rx_rtn = sl_core_mac_rx_stop(ldev_num, lgrp_num, mac_num); + if (rx_rtn) { + /* Log error, but allow the function to continue execution */ + sl_ctrl_log_err_trace(ctrl_mac, LOG_NAME, "core_mac_rx_stop failed [%d]", rx_rtn); + } + + if (tx_rtn && rx_rtn) { + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RESET_FAIL); + rtn = -EIO; + } else { + SL_CTRL_MAC_COUNTER_INC(ctrl_mac, MAC_RESET); + } + + sl_ctrl_mac_put(ctrl_mac); + + return rtn; +} + +int sl_ctrl_mac_info_map_get(u8 ldev_num, u8 lgrp_num, u8 mac_num, u64 *info_map) +{ + struct sl_ctrl_mac *ctrl_mac; + + ctrl_mac = sl_ctrl_mac_get(ldev_num, lgrp_num, mac_num); + if (!ctrl_mac) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info map get NULL mac (ldev_num = %u, lgrp_num = %u, mac_num = %u)", + ldev_num, lgrp_num, mac_num); + return -EBADRQC; + } + + if (!sl_ctrl_mac_kref_get_unless_zero(ctrl_mac)) { + sl_ctrl_log_err(NULL, LOG_NAME, + "info map get kref_get_unless_zero failed (ctrl_mac = 0x%p)", + ctrl_mac); + return -EBADRQC; + } + + *info_map = sl_core_mac_info_map_get(ldev_num, lgrp_num, mac_num); + + sl_ctrl_log_dbg(ctrl_mac, LOG_NAME, "info map get (info_map = 0x%llX)", *info_map); + + if (sl_ctrl_mac_put(ctrl_mac)) + sl_ctrl_log_dbg(NULL, LOG_NAME, + "info map get - mac removed (ctrl_mac = 0x%p)", ctrl_mac); + + return 0; +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac_counters.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac_counters.c new file mode 100644 index 000000000000..a30e64f80122 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_mac_counters.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025-2026 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include + +#include "sl_ctrl_mac.h" +#include "sl_ctrl_mac_counters.h" + +#define SL_CTRL_MAC_COUNTER_INIT(_mac, _counter) \ + (_mac)->counters[_counter].name = #_counter + +int sl_ctrl_mac_counters_init(struct sl_ctrl_mac *ctrl_mac) +{ + if (ctrl_mac->counters) + return -EALREADY; + + ctrl_mac->counters = kzalloc(sizeof(*ctrl_mac->counters) * SL_CTRL_MAC_COUNTERS_COUNT, GFP_KERNEL); + if (!ctrl_mac->counters) + return -ENOMEM; + + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_START_CMD); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_STARTED); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_START_FAIL); + + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_STOP_CMD); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_STOPPED); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_TX_STOP_FAIL); + + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_START_CMD); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_STARTED); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_START_FAIL); + + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_STOP_CMD); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_STOPPED); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RX_STOP_FAIL); + + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RESET_CMD); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RESET); + SL_CTRL_MAC_COUNTER_INIT(ctrl_mac, MAC_RESET_FAIL); + + return 0; +} + +int sl_ctrl_mac_counter_get(struct sl_ctrl_mac *ctrl_mac, u32 counter, int *count) +{ + *count = atomic_read(&ctrl_mac->counters[counter].count); + + return 0; +} + +void sl_ctrl_mac_counters_del(struct sl_ctrl_mac *ctrl_mac) +{ + kfree(ctrl_mac->counters); +} diff --git a/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_media_counters.c b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_media_counters.c new file mode 100644 index 000000000000..0f12a479d017 --- /dev/null +++ b/drivers/net/ethernet/hpe/sl/ctrl/sl_ctrl_media_counters.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2025,2026 Hewlett Packard Enterprise Development LP */ + +#include +#include +#include + +#include "sl_media_jack.h" +#include "sl_ctrl_media_counters.h" +#include "base/sl_ctrl_log.h" + +#define LOG_NAME SL_CTRL_MEDIA_LOG_NAME + +#define SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(_media, _counter) \ + (_media)->cause_counters[_counter].name = #_counter + +int sl_ctrl_media_cause_counters_init(struct sl_media_jack *media_jack) +{ + sl_ctrl_log_dbg(media_jack, LOG_NAME, "media cause counters init"); + + media_jack->cause_counters = kzalloc(sizeof(*media_jack->cause_counters) * + SL_CTRL_MEDIA_CAUSE_COUNTERS_COUNT, GFP_KERNEL); + if (!media_jack->cause_counters) + return -ENOMEM; + + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_EEPROM_FORMAT_UNSUPPORTED); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_EEPROM_VENDOR_UNSUPPORTED); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_EEPROM_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_ONLINE_STATUS_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_ONLINE_TIMEDOUT); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_ONLINE_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_ONLINE_JACK_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SERDES_SETTINGS_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SCAN_STATUS_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SCAN_HDL_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SCAN_JACK_GET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_MEDIA_ATTR_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_INTR_EVENT_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_POWER_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO_LOW_POWER_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO_HIGH_POWER_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO_LOW_POWER_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO_HIGH_POWER_SET); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_SHIFT_STATE_JACK_IO); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_OFFLINE); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_HOT); + SL_CTRL_MEDIA_CAUSE_COUNTER_INIT(media_jack, MEDIA_CAUSE_WARM); + + return 0; +} + +int sl_ctrl_media_cause_counter_get(struct sl_media_jack *media_jack, u32 counter, int *count) +{ + *count = atomic_read(&media_jack->cause_counters[counter].count); + + sl_ctrl_log_dbg(media_jack, LOG_NAME, "get (cause counter = %u %s, count = %d)", counter, + media_jack->cause_counters[counter].name, *count); + + return 0; +} + +void sl_ctrl_media_cause_counters_del(struct sl_media_jack *media_jack) +{ + kfree(media_jack->cause_counters); +} + +void sl_ctrl_media_cause_counter_inc(struct sl_media_jack *media_jack, unsigned long cause_map) +{ + unsigned long which; + + sl_ctrl_log_dbg(media_jack, LOG_NAME, "media cause counter inc"); + + BUILD_BUG_ON(SL_CTRL_MEDIA_CAUSE_COUNTERS_COUNT > 32); + + for_each_set_bit(which, &cause_map, sizeof(cause_map) * BITS_PER_BYTE) { + switch (BIT(which)) { + case SL_MEDIA_FAULT_CAUSE_EEPROM_FORMAT_UNSUPPORTED: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_EEPROM_FORMAT_UNSUPPORTED); + break; + case SL_MEDIA_FAULT_CAUSE_EEPROM_VENDOR_UNSUPPORTED: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_EEPROM_VENDOR_UNSUPPORTED); + break; + case SL_MEDIA_FAULT_CAUSE_EEPROM_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_EEPROM_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_ONLINE_STATUS_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_ONLINE_STATUS_GET); + break; + case SL_MEDIA_FAULT_CAUSE_ONLINE_TIMEDOUT: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_ONLINE_TIMEDOUT); + break; + case SL_MEDIA_FAULT_CAUSE_ONLINE_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_ONLINE_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_ONLINE_JACK_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_ONLINE_JACK_GET); + break; + case SL_MEDIA_FAULT_CAUSE_SERDES_SETTINGS_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SERDES_SETTINGS_GET); + break; + case SL_MEDIA_FAULT_CAUSE_SCAN_STATUS_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SCAN_STATUS_GET); + break; + case SL_MEDIA_FAULT_CAUSE_SCAN_HDL_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SCAN_HDL_GET); + break; + case SL_MEDIA_FAULT_CAUSE_SCAN_JACK_GET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SCAN_JACK_GET); + break; + case SL_MEDIA_FAULT_CAUSE_MEDIA_ATTR_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_MEDIA_ATTR_SET); + break; + case SL_MEDIA_FAULT_CAUSE_INTR_EVENT_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_INTR_EVENT_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_POWER_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_POWER_SET); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_DOWN_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_DOWN_JACK_IO_LOW_POWER_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO_LOW_POWER_SET); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_DOWN_JACK_IO_HIGH_POWER_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_DOWN_JACK_IO_HIGH_POWER_SET); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_UP_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_UP_JACK_IO_LOW_POWER_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO_LOW_POWER_SET); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_UP_JACK_IO_HIGH_POWER_SET: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_UP_JACK_IO_HIGH_POWER_SET); + break; + case SL_MEDIA_FAULT_CAUSE_SHIFT_STATE_JACK_IO: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_SHIFT_STATE_JACK_IO); + break; + case SL_MEDIA_FAULT_CAUSE_OFFLINE: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_OFFLINE); + break; + case SL_MEDIA_FAULT_CAUSE_HOT: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_HOT); + break; + case SL_MEDIA_FAULT_CAUSE_WARM: + SL_CTRL_MEDIA_CAUSE_COUNTER_INC(media_jack, MEDIA_CAUSE_WARM); + break; + default: + sl_ctrl_log_warn_trace(media_jack, LOG_NAME, + "cause_counter_inc unknown fault cause (bit = %lu)", which); + break; + } + } +} -- 2.56.0