freebsd-skq/sys/dev/al_eth/al_init_eth_lm.c
Wojciech Macek 7902c8dca8 Driver for PCI Ethernet NIC on Alpine V1 and V2.
Obtained from:         Semihalf
Submitted by:          Michal Stanek <mst@semihalf.com>
Sponsored by:          Annapurna Labs
Reviewed by:           wma
Differential Revision: https://reviews.freebsd.org/D7814
2016-10-20 11:31:11 +00:00

1538 lines
43 KiB
C

/*-
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
* All rights reserved.
*
* Developed by Semihalf.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "al_init_eth_lm.h"
#include "al_serdes.h"
#include "al_hal_eth.h"
#include "al_init_eth_kr.h"
/**
* @{
* @file al_init_eth_lm.c
*
* @brief ethernet link management common utilities
*
*/
/* delay before checking link status with new serdes parameters (uSec) */
#define AL_ETH_LM_LINK_STATUS_DELAY 1000
/* delay before checking link status after reconfiguring the retimer (uSec) */
#define AL_ETH_LM_RETIMER_LINK_STATUS_DELAY 50000
#define AL_ETH_LM_EQ_ITERATIONS 15
#define AL_ETH_LM_MAX_DCGAIN 8
/* num of link training failures till serdes reset */
#define AL_ETH_LT_FAILURES_TO_RESET 10
#define MODULE_IDENTIFIER_IDX 0
#define MODULE_IDENTIFIER_SFP 0x3
#define MODULE_IDENTIFIER_QSFP 0xd
#define SFP_PRESENT 0
#define SFP_NOT_PRESENT 1
/* SFP+ module */
#define SFP_I2C_HEADER_10G_IDX 3
#define SFP_I2C_HEADER_10G_DA_IDX 8
#define SFP_I2C_HEADER_10G_DA_LEN_IDX 18
#define SFP_I2C_HEADER_1G_IDX 6
#define SFP_I2C_HEADER_SIGNAL_RATE 12 /* Nominal signaling rate, units of 100MBd. */
#define SFP_MIN_SIGNAL_RATE_25G 250
#define SFP_MIN_SIGNAL_RATE_10G 100
/* QSFP+ module */
#define QSFP_COMPLIANCE_CODE_IDX 131
/* 40GBASE-LR4 and 40GBASE-SR4 are optic modules */
#define QSFP_COMPLIANCE_CODE_OPTIC ((1 << 1) | (1 << 2))
#define QSFP_COMPLIANCE_CODE_DAC (1 << 3)
#define QSFP_CABLE_LEN_IDX 146
/* TODO: need to check the necessary delay */
#define AL_ETH_LM_RETIMER_WAIT_FOR_LOCK 500 /* delay after retimer reset to lock (mSec) */
#define AL_ETH_LM_SERDES_WAIT_FOR_LOCK 50 /* delay after signal detect to lock (mSec) */
#define AL_ETH_LM_GEARBOX_RESET_DELAY 1000 /* (uSec) */
static const uint32_t
al_eth_retimer_boost_addr[AL_ETH_RETIMER_CHANNEL_MAX][AL_ETH_RETIMER_TYPE_MAX] = {
/* BR_210 | BR_410 */
/* AL_ETH_RETIMER_CHANNEL_A */ {0xf, 0x1a},
/* AL_ETH_RETIMER_CHANNEL_B */ {0x16, 0x18},
/* AL_ETH_RETIMER_CHANNEL_C */ {0x0, 0x16},
/* AL_ETH_RETIMER_CHANNEL_D */ {0x0, 0x14},
};
#define RETIMER_LENS_MAX 5
static const uint32_t
al_eth_retimer_boost_lens[RETIMER_LENS_MAX] = {0, 1, 2, 3, 5};
static const uint32_t
al_eth_retimer_boost_value[RETIMER_LENS_MAX + 1][AL_ETH_RETIMER_TYPE_MAX] = {
/* BR_210 | BR_410 */
/* 0 */ {0x0, 0x0},
/* 1 */ {0x1, 0x1},
/* 2 */ {0x2, 0x1},
/* 3 */ {0x3, 0x3},
/* 5 */ {0x7, 0x3},
/* 5+ */{0xb, 0x7},
};
struct retimer_config_reg {
uint8_t addr;
uint8_t value;
uint8_t mask;
};
static struct retimer_config_reg retimer_ds25_25g_mode_tx_ch[] = {
{.addr = 0x0A, .value = 0x0C, .mask = 0xff },
{.addr = 0x2F, .value = 0x54, .mask = 0xff },
{.addr = 0x31, .value = 0x20, .mask = 0xff },
{.addr = 0x1E, .value = 0xE9, .mask = 0xff },
{.addr = 0x1F, .value = 0x0B, .mask = 0xff },
{.addr = 0xA6, .value = 0x43, .mask = 0xff },
{.addr = 0x2A, .value = 0x5A, .mask = 0xff },
{.addr = 0x2B, .value = 0x0A, .mask = 0xff },
{.addr = 0x2C, .value = 0xF6, .mask = 0xff },
{.addr = 0x70, .value = 0x05, .mask = 0xff },
{.addr = 0x6A, .value = 0x21, .mask = 0xff },
{.addr = 0x35, .value = 0x0F, .mask = 0xff },
{.addr = 0x12, .value = 0x83, .mask = 0xff },
{.addr = 0x9C, .value = 0x24, .mask = 0xff },
{.addr = 0x98, .value = 0x00, .mask = 0xff },
{.addr = 0x42, .value = 0x50, .mask = 0xff },
{.addr = 0x44, .value = 0x90, .mask = 0xff },
{.addr = 0x45, .value = 0xC0, .mask = 0xff },
{.addr = 0x46, .value = 0xD0, .mask = 0xff },
{.addr = 0x47, .value = 0xD1, .mask = 0xff },
{.addr = 0x48, .value = 0xD5, .mask = 0xff },
{.addr = 0x49, .value = 0xD8, .mask = 0xff },
{.addr = 0x4A, .value = 0xEA, .mask = 0xff },
{.addr = 0x4B, .value = 0xF7, .mask = 0xff },
{.addr = 0x4C, .value = 0xFD, .mask = 0xff },
{.addr = 0x8E, .value = 0x00, .mask = 0xff },
{.addr = 0x3D, .value = 0x94, .mask = 0xff },
{.addr = 0x3F, .value = 0x40, .mask = 0xff },
{.addr = 0x3E, .value = 0x43, .mask = 0xff },
{.addr = 0x0A, .value = 0x00, .mask = 0xff },
};
static struct retimer_config_reg retimer_ds25_25g_mode_rx_ch[] = {
{.addr = 0x0A, .value = 0x0C, .mask = 0xff},
{.addr = 0x2F, .value = 0x54, .mask = 0xff},
{.addr = 0x31, .value = 0x40, .mask = 0xff},
{.addr = 0x1E, .value = 0xE3, .mask = 0xff},
{.addr = 0x1F, .value = 0x0B, .mask = 0xff},
{.addr = 0xA6, .value = 0x43, .mask = 0xff},
{.addr = 0x2A, .value = 0x5A, .mask = 0xff},
{.addr = 0x2B, .value = 0x0A, .mask = 0xff},
{.addr = 0x2C, .value = 0xF6, .mask = 0xff},
{.addr = 0x70, .value = 0x05, .mask = 0xff},
{.addr = 0x6A, .value = 0x21, .mask = 0xff},
{.addr = 0x35, .value = 0x0F, .mask = 0xff},
{.addr = 0x12, .value = 0x83, .mask = 0xff},
{.addr = 0x9C, .value = 0x24, .mask = 0xff},
{.addr = 0x98, .value = 0x00, .mask = 0xff},
{.addr = 0x42, .value = 0x50, .mask = 0xff},
{.addr = 0x44, .value = 0x90, .mask = 0xff},
{.addr = 0x45, .value = 0xC0, .mask = 0xff},
{.addr = 0x46, .value = 0xD0, .mask = 0xff},
{.addr = 0x47, .value = 0xD1, .mask = 0xff},
{.addr = 0x48, .value = 0xD5, .mask = 0xff},
{.addr = 0x49, .value = 0xD8, .mask = 0xff},
{.addr = 0x4A, .value = 0xEA, .mask = 0xff},
{.addr = 0x4B, .value = 0xF7, .mask = 0xff},
{.addr = 0x4C, .value = 0xFD, .mask = 0xff},
{.addr = 0x8E, .value = 0x00, .mask = 0xff},
{.addr = 0x3D, .value = 0x94, .mask = 0xff},
{.addr = 0x3F, .value = 0x40, .mask = 0xff},
{.addr = 0x3E, .value = 0x43, .mask = 0xff},
{.addr = 0x0A, .value = 0x00, .mask = 0xff},
};
static struct retimer_config_reg retimer_ds25_10g_mode[] = {
/* Assert CDR reset (6.3) */
{.addr = 0x0A, .value = 0x0C, .mask = 0x0C},
/* Select 10.3125Gbps standard rate mode (6.6) */
{.addr = 0x2F, .value = 0x00, .mask = 0xF0},
/* Enable loop filter auto-adjust */
{.addr = 0x1F, .value = 0x08, .mask = 0x08},
/* Set Adapt Mode 1 (6.13) */
{.addr = 0x31, .value = 0x20, .mask = 0x60},
/* Disable the DFE since most applications do not need it (6.18) */
{.addr = 0x1E, .value = 0x08, .mask = 0x08},
/* Release CDR reset (6.4) */
{.addr = 0x0A, .value = 0x00, .mask = 0x0C},
/* Enable FIR (6.12) */
{.addr = 0x3D, .value = 0x80, .mask = 0x80},
/* Set Main-cursor tap sign to positive (6.12) */
{.addr = 0x3D, .value = 0x00, .mask = 0x40},
/* Set Post-cursor tap sign to negative (6.12) */
{.addr = 0x3F, .value = 0x40, .mask = 0x40},
/* Set Pre-cursor tap sign to negative (6.12) */
{.addr = 0x3E, .value = 0x40, .mask = 0x40},
/* Set Main-cursor tap magnitude to 13 (6.12) */
{.addr = 0x3D, .value = 0x0D, .mask = 0x1F},
};
static int al_eth_lm_retimer_boost_config(struct al_eth_lm_context *lm_context);
static int al_eth_lm_retimer_ds25_full_config(struct al_eth_lm_context *lm_context);
static al_bool al_eth_lm_retimer_ds25_signal_detect(
struct al_eth_lm_context *lm_context, uint32_t channel);
static int al_eth_lm_retimer_ds25_cdr_reset(struct al_eth_lm_context *lm_context, uint32_t channel);
static al_bool al_eth_lm_retimer_ds25_cdr_lock(
struct al_eth_lm_context *lm_context, uint32_t channel);
static int al_eth_lm_retimer_25g_rx_adaptation(struct al_eth_lm_context *lm_context);
struct al_eth_lm_retimer {
int (*config)(struct al_eth_lm_context *lm_context);
int (*reset)(struct al_eth_lm_context *lm_context, uint32_t channel);
int (*signal_detect)(struct al_eth_lm_context *lm_context, uint32_t channel);
int (*cdr_lock)(struct al_eth_lm_context *lm_context, uint32_t channel);
int (*rx_adaptation)(struct al_eth_lm_context *lm_context);
};
static struct al_eth_lm_retimer retimer[] = {
{.config = al_eth_lm_retimer_boost_config, .signal_detect = NULL,
.reset = NULL, .cdr_lock = NULL, .rx_adaptation = NULL},
{.config = al_eth_lm_retimer_boost_config, .signal_detect = NULL,
.reset = NULL, .cdr_lock = NULL, .rx_adaptation = NULL},
{.config = al_eth_lm_retimer_ds25_full_config,
.signal_detect = al_eth_lm_retimer_ds25_signal_detect,
.reset = al_eth_lm_retimer_ds25_cdr_reset,
.cdr_lock = al_eth_lm_retimer_ds25_cdr_lock,
.rx_adaptation = al_eth_lm_retimer_25g_rx_adaptation},
};
#define SFP_10G_DA_ACTIVE 0x8
#define SFP_10G_DA_PASSIVE 0x4
#define lm_debug(...) \
do { \
if (lm_context->debug) \
al_warn(__VA_ARGS__); \
else \
al_dbg(__VA_ARGS__); \
} while (0)
static int
al_eth_sfp_detect(struct al_eth_lm_context *lm_context,
enum al_eth_lm_link_mode *new_mode)
{
int rc = 0;
uint8_t sfp_10g;
uint8_t sfp_1g;
uint8_t sfp_cable_tech;
uint8_t sfp_da_len;
uint8_t signal_rate;
do {
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
SFP_I2C_HEADER_10G_IDX, &sfp_10g);
if (rc != 0)
break;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
SFP_I2C_HEADER_1G_IDX, &sfp_1g);
if (rc != 0)
break;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
SFP_I2C_HEADER_10G_DA_IDX, &sfp_cable_tech);
if (rc != 0)
break;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
SFP_I2C_HEADER_10G_DA_LEN_IDX, &sfp_da_len);
if (rc != 0)
break;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id,
lm_context->sfp_i2c_addr,
SFP_I2C_HEADER_SIGNAL_RATE,
&signal_rate);
} while (0);
if (rc != 0) {
if (rc == ETIMEDOUT) {
/* ETIMEDOUT is returned when no SFP is connected */
if (lm_context->mode != AL_ETH_LM_MODE_DISCONNECTED)
lm_debug("%s: SFP Disconnected\n", __func__);
*new_mode = AL_ETH_LM_MODE_DISCONNECTED;
} else {
return (rc);
}
} else if ((sfp_cable_tech & (SFP_10G_DA_PASSIVE | SFP_10G_DA_ACTIVE)) != 0) {
if ((signal_rate >= SFP_MIN_SIGNAL_RATE_25G) &&
((lm_context->max_speed == AL_ETH_LM_MAX_SPEED_25G) ||
(lm_context->max_speed == AL_ETH_LM_MAX_SPEED_MAX)))
*new_mode = AL_ETH_LM_MODE_25G;
else if ((signal_rate >= SFP_MIN_SIGNAL_RATE_10G) &&
((lm_context->max_speed == AL_ETH_LM_MAX_SPEED_10G) ||
(lm_context->max_speed == AL_ETH_LM_MAX_SPEED_MAX)))
*new_mode = AL_ETH_LM_MODE_10G_DA;
else
*new_mode = AL_ETH_LM_MODE_1G;
lm_debug("%s: %s DAC (%d M) detected (max signal rate %d)\n",
__func__,
(sfp_cable_tech & SFP_10G_DA_PASSIVE) ? "Passive" : "Active",
sfp_da_len,
signal_rate);
/* for active direct attached need to use len 0 in the retimer configuration */
lm_context->da_len = (sfp_cable_tech & SFP_10G_DA_PASSIVE) ? sfp_da_len : 0;
} else if (sfp_10g != 0) {
lm_debug("%s: 10 SFP detected\n", __func__);
*new_mode = AL_ETH_LM_MODE_10G_OPTIC;
} else if (sfp_1g != 0) {
lm_debug("%s: 1G SFP detected\n", __func__);
*new_mode = AL_ETH_LM_MODE_1G;
} else {
al_warn("%s: unknown SFP inserted. eeprom content: 10G compliance 0x%x,"
" 1G compliance 0x%x, sfp+cable 0x%x. default to %s\n",
__func__, sfp_10g, sfp_1g, sfp_cable_tech,
al_eth_lm_mode_convert_to_str(lm_context->default_mode));
*new_mode = lm_context->default_mode;
lm_context->da_len = lm_context->default_dac_len;
}
if ((lm_context->sfp_detect_force_mode) && (*new_mode != AL_ETH_LM_MODE_DISCONNECTED) &&
(*new_mode != lm_context->default_mode)) {
al_warn("%s: Force mode to default (%s). mode based of the SFP EEPROM %s\n",
__func__, al_eth_lm_mode_convert_to_str(lm_context->default_mode),
al_eth_lm_mode_convert_to_str(*new_mode));
*new_mode = lm_context->default_mode;
}
lm_context->mode = *new_mode;
return (0);
}
static int
al_eth_qsfp_detect(struct al_eth_lm_context *lm_context,
enum al_eth_lm_link_mode *new_mode)
{
int rc = 0;
uint8_t qsfp_comp_code;
uint8_t qsfp_da_len;
do {
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
QSFP_COMPLIANCE_CODE_IDX, &qsfp_comp_code);
if (rc != 0)
break;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
QSFP_CABLE_LEN_IDX, &qsfp_da_len);
if (rc != 0)
break;
} while (0);
if (rc != 0) {
if (rc == ETIMEDOUT) {
/* ETIMEDOUT is returned when no SFP is connected */
lm_debug("%s: SFP Disconnected\n", __func__);
*new_mode = AL_ETH_LM_MODE_DISCONNECTED;
} else {
return (rc);
}
} else if ((qsfp_comp_code & QSFP_COMPLIANCE_CODE_DAC) != 0) {
lm_debug("%s: 10G passive DAC (%d M) detected\n",
__func__, qsfp_da_len);
*new_mode = AL_ETH_LM_MODE_10G_DA;
lm_context->da_len = qsfp_da_len;
} else if ((qsfp_comp_code & QSFP_COMPLIANCE_CODE_OPTIC) != 0) {
lm_debug("%s: 10G optic module detected\n", __func__);
*new_mode = AL_ETH_LM_MODE_10G_OPTIC;
} else {
al_warn("%s: unknown QSFP inserted. eeprom content: 10G "
"compliance 0x%x default to %s\n", __func__, qsfp_comp_code,
al_eth_lm_mode_convert_to_str(lm_context->default_mode));
*new_mode = lm_context->default_mode;
lm_context->da_len = lm_context->default_dac_len;
}
lm_context->mode = *new_mode;
return (0);
}
static int
al_eth_module_detect(struct al_eth_lm_context *lm_context,
enum al_eth_lm_link_mode *new_mode)
{
int rc = 0;
uint8_t module_idx;
int sfp_present = SFP_PRESENT;
if ((lm_context->gpio_get) && (lm_context->gpio_present != 0))
sfp_present = lm_context->gpio_get(lm_context->gpio_present);
if (sfp_present == SFP_NOT_PRESENT) {
lm_debug("%s: SFP not exist\n", __func__);
*new_mode = AL_ETH_LM_MODE_DISCONNECTED;
return 0;
}
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->sfp_bus_id, lm_context->sfp_i2c_addr,
MODULE_IDENTIFIER_IDX, &module_idx);
if (rc != 0) {
if (rc == ETIMEDOUT) {
/* ETIMEDOUT is returned when no SFP is connected */
if (lm_context->mode != AL_ETH_LM_MODE_DISCONNECTED)
lm_debug("%s: SFP Disconnected\n", __func__);
*new_mode = AL_ETH_LM_MODE_DISCONNECTED;
return (0);
} else {
return (rc);
}
}
if (module_idx == MODULE_IDENTIFIER_QSFP)
return (al_eth_qsfp_detect(lm_context, new_mode));
else
return (al_eth_sfp_detect(lm_context, new_mode));
return (0);
}
static struct al_serdes_adv_tx_params da_tx_params = {
.override = TRUE,
.amp = 0x1,
.total_driver_units = 0x13,
.c_plus_1 = 0x2,
.c_plus_2 = 0,
.c_minus_1 = 0x2,
.slew_rate = 0,
};
static struct al_serdes_adv_rx_params da_rx_params = {
.override = TRUE,
.dcgain = 0x4,
.dfe_3db_freq = 0x4,
.dfe_gain = 0x3,
.dfe_first_tap_ctrl = 0x5,
.dfe_secound_tap_ctrl = 0x1,
.dfe_third_tap_ctrl = 0x8,
.dfe_fourth_tap_ctrl = 0x1,
.low_freq_agc_gain = 0x7,
.precal_code_sel = 0,
.high_freq_agc_boost = 0x1d,
};
static struct al_serdes_adv_tx_params optic_tx_params = {
.override = TRUE,
.amp = 0x1,
.total_driver_units = 0x13,
.c_plus_1 = 0x2,
.c_plus_2 = 0,
.c_minus_1 = 0,
.slew_rate = 0,
};
static struct al_serdes_adv_rx_params optic_rx_params = {
.override = TRUE,
.dcgain = 0x0,
.dfe_3db_freq = 0x7,
.dfe_gain = 0x0,
.dfe_first_tap_ctrl = 0x0,
.dfe_secound_tap_ctrl = 0x8,
.dfe_third_tap_ctrl = 0x0,
.dfe_fourth_tap_ctrl = 0x8,
.low_freq_agc_gain = 0x7,
.precal_code_sel = 0,
.high_freq_agc_boost = 0x4,
};
static void
al_eth_serdes_static_tx_params_set(struct al_eth_lm_context *lm_context)
{
if (lm_context->tx_param_dirty == 0)
return;
if (lm_context->serdes_tx_params_valid != 0) {
lm_context->tx_param_dirty = 0;
lm_context->tx_params_override.override = TRUE;
if ((lm_context->serdes_obj->tx_advanced_params_set) == 0) {
al_err("tx_advanced_params_set is not supported for this serdes group\n");
return;
}
lm_context->serdes_obj->tx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&lm_context->tx_params_override);
} else if (lm_context->static_values != 0) {
lm_context->tx_param_dirty = 0;
if ((lm_context->serdes_obj->tx_advanced_params_set) == 0) {
al_err("tx_advanced_params_set is not supported for this serdes group\n");
return;
}
if ((lm_context->retimer_exist == 0) &&
(lm_context->mode == AL_ETH_LM_MODE_10G_DA))
lm_context->serdes_obj->tx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&da_tx_params);
else
lm_context->serdes_obj->tx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&optic_tx_params);
}
}
static void
al_eth_serdes_static_rx_params_set(struct al_eth_lm_context *lm_context)
{
if (lm_context->rx_param_dirty == 0)
return;
if (lm_context->serdes_rx_params_valid != 0) {
lm_context->rx_param_dirty = 0;
lm_context->rx_params_override.override = TRUE;
if ((lm_context->serdes_obj->rx_advanced_params_set) == 0) {
al_err("rx_advanced_params_set is not supported for this serdes group\n");
return;
}
lm_context->serdes_obj->rx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&lm_context->rx_params_override);
} else if (lm_context->static_values != 0) {
lm_context->rx_param_dirty = 0;
if ((lm_context->serdes_obj->rx_advanced_params_set) == 0) {
al_err("rx_advanced_params_set is not supported for this serdes group\n");
return;
}
if ((lm_context->retimer_exist == 0) &&
(lm_context->mode == AL_ETH_LM_MODE_10G_DA))
lm_context->serdes_obj->rx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&da_rx_params);
else
lm_context->serdes_obj->rx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&optic_rx_params);
}
}
static int
al_eth_rx_equal_run(struct al_eth_lm_context *lm_context)
{
struct al_serdes_adv_rx_params rx_params;
int dcgain;
int best_dcgain = -1;
int i;
int best_score = -1;
int test_score = -1;
rx_params.override = FALSE;
lm_context->serdes_obj->rx_advanced_params_set(lm_context->serdes_obj,
lm_context->lane, &rx_params);
lm_debug("score | dcgain | dfe3db | dfegain | tap1 | tap2 | tap3 | "
"tap4 | low freq | high freq\n");
for (dcgain = 0; dcgain < AL_ETH_LM_MAX_DCGAIN; dcgain++) {
lm_context->serdes_obj->dcgain_set(
lm_context->serdes_obj,
dcgain);
test_score = lm_context->serdes_obj->rx_equalization(
lm_context->serdes_obj,
lm_context->lane);
if (test_score < 0) {
al_warn("serdes rx equalization failed on error\n");
return (test_score);
}
if (test_score > best_score) {
best_score = test_score;
best_dcgain = dcgain;
}
lm_context->serdes_obj->rx_advanced_params_get(
lm_context->serdes_obj,
lm_context->lane,
&rx_params);
lm_debug("%6d|%8x|%8x|%9x|%6x|%6x|%6x|%6x|%10x|%10x|\n",
test_score, rx_params.dcgain, rx_params.dfe_3db_freq,
rx_params.dfe_gain, rx_params.dfe_first_tap_ctrl,
rx_params.dfe_secound_tap_ctrl, rx_params.dfe_third_tap_ctrl,
rx_params.dfe_fourth_tap_ctrl, rx_params.low_freq_agc_gain,
rx_params.high_freq_agc_boost);
}
lm_context->serdes_obj->dcgain_set(
lm_context->serdes_obj,
best_dcgain);
best_score = -1;
for(i = 0; i < AL_ETH_LM_EQ_ITERATIONS; i++) {
test_score = lm_context->serdes_obj->rx_equalization(
lm_context->serdes_obj,
lm_context->lane);
if (test_score < 0) {
al_warn("serdes rx equalization failed on error\n");
return (test_score);
}
if (test_score > best_score) {
best_score = test_score;
lm_context->serdes_obj->rx_advanced_params_get(
lm_context->serdes_obj,
lm_context->lane,
&rx_params);
}
}
rx_params.precal_code_sel = 0;
rx_params.override = TRUE;
lm_context->serdes_obj->rx_advanced_params_set(
lm_context->serdes_obj,
lm_context->lane,
&rx_params);
lm_debug("-------------------- best dcgain %d ------------------------------------\n", best_dcgain);
lm_debug("%6d|%8x|%8x|%9x|%6x|%6x|%6x|%6x|%10x|%10x|\n",
best_score, rx_params.dcgain, rx_params.dfe_3db_freq,
rx_params.dfe_gain, rx_params.dfe_first_tap_ctrl,
rx_params.dfe_secound_tap_ctrl, rx_params.dfe_third_tap_ctrl,
rx_params.dfe_fourth_tap_ctrl, rx_params.low_freq_agc_gain,
rx_params.high_freq_agc_boost);
return (0);
}
static int al_eth_lm_retimer_boost_config(struct al_eth_lm_context *lm_context)
{
int i;
int rc = 0;
uint8_t boost = 0;
uint32_t boost_addr =
al_eth_retimer_boost_addr[lm_context->retimer_channel][lm_context->retimer_type];
if (lm_context->mode != AL_ETH_LM_MODE_10G_DA) {
boost = al_eth_retimer_boost_value[0][lm_context->retimer_type];
} else {
for (i = 0; i < RETIMER_LENS_MAX; i++) {
if (lm_context->da_len <= al_eth_retimer_boost_lens[i]) {
boost = al_eth_retimer_boost_value[i][lm_context->retimer_type];
break;
}
}
if (i == RETIMER_LENS_MAX)
boost = al_eth_retimer_boost_value[RETIMER_LENS_MAX][lm_context->retimer_type];
}
lm_debug("config retimer boost in channel %d (addr %x) to 0x%x\n",
lm_context->retimer_channel, boost_addr, boost);
rc = lm_context->i2c_write(lm_context->i2c_context,
lm_context->retimer_bus_id, lm_context->retimer_i2c_addr,
boost_addr, boost);
if (rc != 0) {
al_err("%s: Error occurred (%d) while writing retimer "
"configuration (bus-id %x i2c-addr %x)\n",
__func__, rc, lm_context->retimer_bus_id,
lm_context->retimer_i2c_addr);
return (rc);
}
return (0);
}
/*******************************************************************************
************************** retimer DS25 ***************************************
******************************************************************************/
#define LM_DS25_CHANNEL_EN_REG 0xff
#define LM_DS25_CHANNEL_EN_MASK 0x03
#define LM_DS25_CHANNEL_EN_VAL 0x01
#define LM_DS25_CHANNEL_SEL_REG 0xfc
#define LM_DS25_CHANNEL_SEL_MASK 0xff
#define LM_DS25_CDR_RESET_REG 0x0a
#define LM_DS25_CDR_RESET_MASK 0x0c
#define LM_DS25_CDR_RESET_ASSERT 0x0c
#define LM_DS25_CDR_RESET_RELEASE 0x00
#define LM_DS25_SIGNAL_DETECT_REG 0x78
#define LM_DS25_SIGNAL_DETECT_MASK 0x20
#define LM_DS25_CDR_LOCK_REG 0x78
#define LM_DS25_CDR_LOCK_MASK 0x10
#define LM_DS25_DRV_PD_REG 0x15
#define LM_DS25_DRV_PD_MASK 0x08
static int al_eth_lm_retimer_ds25_write_reg(struct al_eth_lm_context *lm_context,
uint8_t reg_addr,
uint8_t reg_mask,
uint8_t reg_value)
{
uint8_t reg;
int rc;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->retimer_bus_id,
lm_context->retimer_i2c_addr,
reg_addr,
&reg);
if (rc != 0)
return (EIO);
reg &= ~(reg_mask);
reg |= reg_value;
rc = lm_context->i2c_write(lm_context->i2c_context,
lm_context->retimer_bus_id,
lm_context->retimer_i2c_addr,
reg_addr,
reg);
if (rc != 0)
return (EIO);
return (0);
}
static int al_eth_lm_retimer_ds25_channel_select(struct al_eth_lm_context *lm_context,
uint8_t channel)
{
int rc = 0;
/* Write to specific channel */
rc = al_eth_lm_retimer_ds25_write_reg(lm_context,
LM_DS25_CHANNEL_EN_REG,
LM_DS25_CHANNEL_EN_MASK,
LM_DS25_CHANNEL_EN_VAL);
if (rc != 0)
return (rc);
rc = al_eth_lm_retimer_ds25_write_reg(lm_context,
LM_DS25_CHANNEL_SEL_REG,
LM_DS25_CHANNEL_SEL_MASK,
(1 << channel));
return (rc);
}
static int al_eth_lm_retimer_ds25_channel_config(struct al_eth_lm_context *lm_context,
uint8_t channel,
struct retimer_config_reg *config,
uint8_t config_size)
{
uint8_t i;
int rc;
rc = al_eth_lm_retimer_ds25_channel_select(lm_context, channel);
if (rc != 0)
goto config_error;
for (i = 0; i < config_size; i++) {
rc = al_eth_lm_retimer_ds25_write_reg(lm_context,
config[i].addr,
config[i].mask,
config[i].value);
if (rc != 0)
goto config_error;
}
lm_debug("%s: retimer channel config done for channel %d\n", __func__, channel);
return (0);
config_error:
al_err("%s: failed to access to the retimer\n", __func__);
return (rc);
}
static int al_eth_lm_retimer_ds25_cdr_reset(struct al_eth_lm_context *lm_context, uint32_t channel)
{
int rc;
lm_debug("Perform CDR reset to channel %d\n", channel);
rc = al_eth_lm_retimer_ds25_channel_select(lm_context, channel);
if (rc)
goto config_error;
rc = al_eth_lm_retimer_ds25_write_reg(lm_context,
LM_DS25_CDR_RESET_REG,
LM_DS25_CDR_RESET_MASK,
LM_DS25_CDR_RESET_ASSERT);
if (rc)
goto config_error;
rc = al_eth_lm_retimer_ds25_write_reg(lm_context,
LM_DS25_CDR_RESET_REG,
LM_DS25_CDR_RESET_MASK,
LM_DS25_CDR_RESET_RELEASE);
if (rc)
goto config_error;
return 0;
config_error:
al_err("%s: failed to access to the retimer\n", __func__);
return rc;
}
static boolean_t al_eth_lm_retimer_ds25_signal_detect(struct al_eth_lm_context *lm_context,
uint32_t channel)
{
int rc = 0;
uint8_t reg;
rc = al_eth_lm_retimer_ds25_channel_select(lm_context, channel);
if (rc)
goto config_error;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->retimer_bus_id,
lm_context->retimer_i2c_addr,
LM_DS25_SIGNAL_DETECT_REG,
&reg);
if (rc)
goto config_error;
if (reg & LM_DS25_SIGNAL_DETECT_MASK)
return TRUE;
return FALSE;
config_error:
al_err("%s: failed to access to the retimer\n", __func__);
return FALSE;
}
static boolean_t al_eth_lm_retimer_ds25_cdr_lock(struct al_eth_lm_context *lm_context,
uint32_t channel)
{
int rc = 0;
uint8_t reg;
rc = al_eth_lm_retimer_ds25_channel_select(lm_context, channel);
if (rc)
goto config_error;
rc = lm_context->i2c_read(lm_context->i2c_context,
lm_context->retimer_bus_id,
lm_context->retimer_i2c_addr,
LM_DS25_CDR_LOCK_REG,
&reg);
if (rc)
goto config_error;
if (reg & LM_DS25_CDR_LOCK_MASK)
return TRUE;
return FALSE;
config_error:
al_err("%s: failed to access to the retimer\n", __func__);
return FALSE;
}
static boolean_t al_eth_lm_wait_for_lock(struct al_eth_lm_context *lm_context,
uint32_t channel)
{
uint32_t timeout = AL_ETH_LM_RETIMER_WAIT_FOR_LOCK;
al_bool lock = AL_FALSE;
while ((timeout > 0) && (lock == FALSE)) {
al_msleep(10);
timeout -= 10;
lock = retimer[lm_context->retimer_type].cdr_lock(lm_context, channel);
}
lm_debug("%s: %s to achieve CDR lock in %d msec\n",
__func__, (lock) ? "succeed" : "FAILED",
(AL_ETH_LM_RETIMER_WAIT_FOR_LOCK - timeout));
return lock;
}
static void al_eth_lm_retimer_signal_lock_check(struct al_eth_lm_context *lm_context,
uint32_t channel,
boolean_t *ready)
{
al_bool signal_detect = TRUE;
al_bool cdr_lock = TRUE;
if (retimer[lm_context->retimer_type].signal_detect) {
if (!retimer[lm_context->retimer_type].signal_detect(lm_context, channel)) {
lm_debug("no signal detected on retimer channel %d\n", channel);
signal_detect = AL_FALSE;
} else {
if (retimer[lm_context->retimer_type].cdr_lock) {
cdr_lock = retimer[lm_context->retimer_type].cdr_lock(
lm_context,
channel);
if (!cdr_lock) {
if (retimer[lm_context->retimer_type].reset) {
retimer[lm_context->retimer_type].reset(lm_context,
channel);
cdr_lock = al_eth_lm_wait_for_lock(lm_context,
channel);
}
}
}
}
}
al_info("%s: (channel %d) signal %d cdr lock %d\n",
__func__, channel, signal_detect, (signal_detect) ? cdr_lock : 0);
*ready = ((cdr_lock == TRUE) && (signal_detect == TRUE));
}
static int al_eth_lm_retimer_ds25_full_config(struct al_eth_lm_context *lm_context)
{
int rc = 0;
al_bool ready;
struct retimer_config_reg *config_tx;
uint32_t config_tx_size;
struct retimer_config_reg *config_rx;
uint32_t config_rx_size;
if (lm_context->mode == AL_ETH_LM_MODE_25G) {
config_tx = retimer_ds25_25g_mode_tx_ch;
config_tx_size = AL_ARR_SIZE(retimer_ds25_25g_mode_tx_ch);
config_rx = retimer_ds25_25g_mode_rx_ch;
config_rx_size = AL_ARR_SIZE(retimer_ds25_25g_mode_rx_ch);
} else {
config_tx = retimer_ds25_10g_mode;
config_tx_size = AL_ARR_SIZE(retimer_ds25_10g_mode);
config_rx = retimer_ds25_10g_mode;
config_rx_size = AL_ARR_SIZE(retimer_ds25_10g_mode);
}
rc = al_eth_lm_retimer_ds25_channel_config(lm_context,
lm_context->retimer_channel,
config_rx,
config_rx_size);
if (rc)
return rc;
rc = al_eth_lm_retimer_ds25_channel_config(lm_context,
lm_context->retimer_tx_channel,
config_tx,
config_tx_size);
if (rc)
return rc;
if (lm_context->serdes_obj->type_get() == AL_SRDS_TYPE_25G) {
lm_debug("%s: serdes 25G - perform tx and rx gearbox reset\n", __func__);
al_eth_gearbox_reset(lm_context->adapter, TRUE, TRUE);
DELAY(AL_ETH_LM_GEARBOX_RESET_DELAY);
}
al_eth_lm_retimer_signal_lock_check(lm_context, lm_context->retimer_tx_channel, &ready);
if (!ready) {
lm_debug("%s: Failed to lock tx channel!\n", __func__);
return (1);
}
lm_debug("%s: retimer full configuration done\n", __func__);
return rc;
}
static int al_eth_lm_retimer_25g_rx_adaptation(struct al_eth_lm_context *lm_context)
{
int rc = 0;
al_bool ready;
al_eth_lm_retimer_signal_lock_check(lm_context, lm_context->retimer_channel, &ready);
if (!ready) {
lm_debug("%s: no signal detected on retimer Rx channel (%d)\n",
__func__, lm_context->retimer_channel);
return rc;
}
al_msleep(AL_ETH_LM_SERDES_WAIT_FOR_LOCK);
return 0;
}
static int al_eth_lm_check_for_link(struct al_eth_lm_context *lm_context, boolean_t *link_up)
{
struct al_eth_link_status status;
int ret = 0;
al_eth_link_status_clear(lm_context->adapter);
al_eth_link_status_get(lm_context->adapter, &status);
if (status.link_up == AL_TRUE) {
lm_debug("%s: >>>> Link state DOWN ==> UP\n", __func__);
al_eth_led_set(lm_context->adapter, AL_TRUE);
lm_context->link_state = AL_ETH_LM_LINK_UP;
*link_up = AL_TRUE;
return 0;
} else if (status.local_fault) {
lm_context->link_state = AL_ETH_LM_LINK_DOWN;
al_eth_led_set(lm_context->adapter, AL_FALSE);
al_err("%s: Failed to establish link\n", __func__);
ret = 1;
} else {
lm_debug("%s: >>>> Link state DOWN ==> DOWN_RF\n", __func__);
lm_context->link_state = AL_ETH_LM_LINK_DOWN_RF;
al_eth_led_set(lm_context->adapter, AL_FALSE);
ret = 0;
}
*link_up = AL_FALSE;
return ret;
}
/*****************************************************************************/
/***************************** API functions *********************************/
/*****************************************************************************/
int
al_eth_lm_init(struct al_eth_lm_context *lm_context,
struct al_eth_lm_init_params *params)
{
lm_context->adapter = params->adapter;
lm_context->serdes_obj = params->serdes_obj;
lm_context->lane = params->lane;
lm_context->sfp_detection = params->sfp_detection;
lm_context->sfp_bus_id = params->sfp_bus_id;
lm_context->sfp_i2c_addr = params->sfp_i2c_addr;
lm_context->retimer_exist = params->retimer_exist;
lm_context->retimer_type = params->retimer_type;
lm_context->retimer_bus_id = params->retimer_bus_id;
lm_context->retimer_i2c_addr = params->retimer_i2c_addr;
lm_context->retimer_channel = params->retimer_channel;
lm_context->retimer_tx_channel = params->retimer_tx_channel;
lm_context->default_mode = params->default_mode;
lm_context->default_dac_len = params->default_dac_len;
lm_context->link_training = params->link_training;
lm_context->rx_equal = params->rx_equal;
lm_context->static_values = params->static_values;
lm_context->i2c_read = params->i2c_read;
lm_context->i2c_write = params->i2c_write;
lm_context->i2c_context = params->i2c_context;
lm_context->get_random_byte = params->get_random_byte;
/* eeprom_read must be provided if sfp_detection is true */
al_assert((lm_context->sfp_detection == FALSE) ||
(lm_context->i2c_read != NULL));
al_assert((lm_context->retimer_exist == FALSE) ||
(lm_context->i2c_write != NULL));
lm_context->local_adv.selector_field = 1;
lm_context->local_adv.capability = 0;
lm_context->local_adv.remote_fault = 0;
lm_context->local_adv.acknowledge = 0;
lm_context->local_adv.next_page = 0;
lm_context->local_adv.technology = AL_ETH_AN_TECH_10GBASE_KR;
lm_context->local_adv.fec_capability = params->kr_fec_enable;
lm_context->mode = AL_ETH_LM_MODE_DISCONNECTED;
lm_context->serdes_tx_params_valid = FALSE;
lm_context->serdes_rx_params_valid = FALSE;
lm_context->rx_param_dirty = 1;
lm_context->tx_param_dirty = 1;
lm_context->gpio_get = params->gpio_get;
lm_context->gpio_present = params->gpio_present;
lm_context->max_speed = params->max_speed;
lm_context->sfp_detect_force_mode = params->sfp_detect_force_mode;
lm_context->lm_pause = params->lm_pause;
lm_context->led_config = params->led_config;
lm_context->retimer_configured = FALSE;
lm_context->link_state = AL_ETH_LM_LINK_DOWN;
return (0);
}
int
al_eth_lm_link_detection(struct al_eth_lm_context *lm_context,
boolean_t *link_fault, enum al_eth_lm_link_mode *old_mode,
enum al_eth_lm_link_mode *new_mode)
{
int err;
struct al_eth_link_status status;
al_assert(lm_context != NULL);
al_assert(old_mode != NULL);
al_assert(new_mode != NULL);
/**
* if Link management is disabled, report no link fault in case the link was up
* before and set new mode to disconnected to avoid calling to link establish
* if the link wasn't up.
*/
if (lm_context->lm_pause != NULL) {
boolean_t lm_pause = lm_context->lm_pause(lm_context->i2c_context);
if (lm_pause == TRUE) {
*new_mode = AL_ETH_LM_MODE_DISCONNECTED;
if (link_fault != 0) {
if (lm_context->link_state == AL_ETH_LM_LINK_UP)
*link_fault = FALSE;
else
*link_fault = TRUE;
}
return 0;
}
}
*old_mode = lm_context->mode;
*new_mode = lm_context->mode;
if (link_fault != NULL)
*link_fault = TRUE;
switch (lm_context->link_state) {
case AL_ETH_LM_LINK_UP:
al_eth_link_status_get(lm_context->adapter, &status);
if (status.link_up) {
if (link_fault != NULL)
*link_fault = FALSE;
al_eth_led_set(lm_context->adapter, TRUE);
return (0);
} else if (status.local_fault) {
lm_debug("%s: >>>> Link state UP ==> DOWN\n", __func__);
lm_context->link_state = AL_ETH_LM_LINK_DOWN;
} else {
lm_debug("%s: >>>> Link state UP ==> DOWN_RF\n", __func__);
lm_context->link_state = AL_ETH_LM_LINK_DOWN_RF;
}
break;
case AL_ETH_LM_LINK_DOWN_RF:
al_eth_link_status_get(lm_context->adapter, &status);
if (status.local_fault) {
lm_debug("%s: >>>> Link state DOWN_RF ==> DOWN\n", __func__);
lm_context->link_state = AL_ETH_LM_LINK_DOWN;
break;
} else if (status.remote_fault == FALSE) {
lm_debug("%s: >>>> Link state DOWN_RF ==> UP\n", __func__);
lm_context->link_state = AL_ETH_LM_LINK_UP;
}
/* in case of remote fault only no need to check SFP again */
return (0);
case AL_ETH_LM_LINK_DOWN:
break;
};
al_eth_led_set(lm_context->adapter, FALSE);
if (lm_context->sfp_detection) {
err = al_eth_module_detect(lm_context, new_mode);
if (err != 0) {
al_err("module_detection failed!\n");
return (err);
}
lm_context->mode = *new_mode;
} else {
lm_context->mode = lm_context->default_mode;
*new_mode = lm_context->mode;
}
if (*old_mode != *new_mode) {
al_info("%s: New SFP mode detected %s -> %s\n",
__func__, al_eth_lm_mode_convert_to_str(*old_mode),
al_eth_lm_mode_convert_to_str(*new_mode));
lm_context->rx_param_dirty = 1;
lm_context->tx_param_dirty = 1;
lm_context->new_port = TRUE;
if ((*new_mode != AL_ETH_LM_MODE_DISCONNECTED) && (lm_context->led_config)) {
struct al_eth_lm_led_config_data data = {0};
switch (*new_mode) {
case AL_ETH_LM_MODE_10G_OPTIC:
case AL_ETH_LM_MODE_10G_DA:
data.speed = AL_ETH_LM_LED_CONFIG_10G;
break;
case AL_ETH_LM_MODE_1G:
data.speed = AL_ETH_LM_LED_CONFIG_1G;
break;
case AL_ETH_LM_MODE_25G:
data.speed = AL_ETH_LM_LED_CONFIG_25G;
break;
default:
al_err("%s: unknown LM mode!\n", __func__);
};
lm_context->led_config(lm_context->i2c_context, &data);
}
}
return (0);
}
int
al_eth_lm_link_establish(struct al_eth_lm_context *lm_context, boolean_t *link_up)
{
boolean_t signal_detected;
int ret = 0;
switch (lm_context->link_state) {
case AL_ETH_LM_LINK_UP:
*link_up = TRUE;
lm_debug("%s: return link up\n", __func__);
return (0);
case AL_ETH_LM_LINK_DOWN_RF:
*link_up = FALSE;
lm_debug("%s: return link down (DOWN_RF)\n", __func__);
return (0);
case AL_ETH_LM_LINK_DOWN:
break;
};
/**
* At this point we will get LM disable only if changed to disable after link detection
* finished. in this case link will not be established until LM will be enable again.
*/
if (lm_context->lm_pause) {
boolean_t lm_pause = lm_context->lm_pause(lm_context->i2c_context);
if (lm_pause == TRUE) {
*link_up = FALSE;
return (0);
}
}
if ((lm_context->new_port) && (lm_context->retimer_exist)) {
al_eth_serdes_static_rx_params_set(lm_context);
al_eth_serdes_static_tx_params_set(lm_context);
#if 0
al_eth_lm_retimer_config(lm_context);
DELAY(AL_ETH_LM_RETIMER_LINK_STATUS_DELAY);
#endif
if (retimer[lm_context->retimer_type].config(lm_context)) {
al_info("%s: failed to configure the retimer\n", __func__);
*link_up = FALSE;
return (1);
}
lm_context->new_port = FALSE;
DELAY(1000);
}
if (lm_context->retimer_exist) {
if (retimer[lm_context->retimer_type].rx_adaptation) {
ret = retimer[lm_context->retimer_type].rx_adaptation(lm_context);
if (ret != 0) {
lm_debug("retimer rx is not ready\n");
*link_up = FALSE;
return (0);
}
}
}
signal_detected = lm_context->serdes_obj->signal_is_detected(
lm_context->serdes_obj,
lm_context->lane);
if (signal_detected == FALSE) {
/* if no signal detected there is nothing to do */
lm_debug("serdes signal is down\n");
*link_up = AL_FALSE;
return 0;
}
if (lm_context->serdes_obj->type_get() == AL_SRDS_TYPE_25G) {
lm_debug("%s: serdes 25G - perform rx gearbox reset\n", __func__);
al_eth_gearbox_reset(lm_context->adapter, FALSE, TRUE);
DELAY(AL_ETH_LM_GEARBOX_RESET_DELAY);
}
if (lm_context->retimer_exist) {
DELAY(AL_ETH_LM_RETIMER_LINK_STATUS_DELAY);
ret = al_eth_lm_check_for_link(lm_context, link_up);
if (ret == 0) {
lm_debug("%s: link is up with retimer\n", __func__);
return 0;
}
return ret;
}
if ((lm_context->mode == AL_ETH_LM_MODE_10G_DA) && (lm_context->link_training)) {
lm_context->local_adv.transmitted_nonce = lm_context->get_random_byte();
lm_context->local_adv.transmitted_nonce &= 0x1f;
ret = al_eth_an_lt_execute(lm_context->adapter,
lm_context->serdes_obj,
lm_context->lane,
&lm_context->local_adv,
&lm_context->partner_adv);
lm_context->rx_param_dirty = 1;
lm_context->tx_param_dirty = 1;
if (ret == 0) {
al_info("%s: link training finished successfully\n", __func__);
lm_context->link_training_failures = 0;
ret = al_eth_lm_check_for_link(lm_context, link_up);
if (ret == 0) {
lm_debug("%s: link is up with LT\n", __func__);
return (0);
}
}
lm_context->link_training_failures++;
if (lm_context->link_training_failures > AL_ETH_LT_FAILURES_TO_RESET) {
lm_debug("%s: failed to establish LT %d times. reset serdes\n",
__func__, AL_ETH_LT_FAILURES_TO_RESET);
lm_context->serdes_obj->pma_hard_reset_lane(
lm_context->serdes_obj,
lm_context->lane,
TRUE);
lm_context->serdes_obj->pma_hard_reset_lane(
lm_context->serdes_obj,
lm_context->lane,
FALSE);
lm_context->link_training_failures = 0;
}
}
al_eth_serdes_static_tx_params_set(lm_context);
if ((lm_context->mode == AL_ETH_LM_MODE_10G_DA) &&
(lm_context->rx_equal)) {
ret = al_eth_rx_equal_run(lm_context);
if (ret == 0) {
DELAY(AL_ETH_LM_LINK_STATUS_DELAY);
ret = al_eth_lm_check_for_link(lm_context, link_up);
if (ret == 0) {
lm_debug("%s: link is up with Rx Equalization\n", __func__);
return (0);
}
}
}
al_eth_serdes_static_rx_params_set(lm_context);
DELAY(AL_ETH_LM_LINK_STATUS_DELAY);
ret = al_eth_lm_check_for_link(lm_context, link_up);
if (ret == 0) {
lm_debug("%s: link is up with static parameters\n", __func__);
return (0);
}
*link_up = FALSE;
return (1);
}
int
al_eth_lm_static_parameters_override(struct al_eth_lm_context *lm_context,
struct al_serdes_adv_tx_params *tx_params,
struct al_serdes_adv_rx_params *rx_params)
{
if (tx_params != NULL) {
lm_context->tx_params_override = *tx_params;
lm_context->tx_param_dirty = 1;
lm_context->serdes_tx_params_valid = TRUE;
}
if (rx_params != NULL) {
lm_context->rx_params_override = *rx_params;
lm_context->rx_param_dirty = 1;
lm_context->serdes_rx_params_valid = TRUE;
}
return (0);
}
int
al_eth_lm_static_parameters_override_disable(struct al_eth_lm_context *lm_context,
boolean_t tx_params, boolean_t rx_params)
{
if (tx_params != 0)
lm_context->serdes_tx_params_valid = FALSE;
if (rx_params != 0)
lm_context->serdes_tx_params_valid = FALSE;
return (0);
}
int
al_eth_lm_static_parameters_get(struct al_eth_lm_context *lm_context,
struct al_serdes_adv_tx_params *tx_params,
struct al_serdes_adv_rx_params *rx_params)
{
if (tx_params != NULL) {
if (lm_context->serdes_tx_params_valid)
*tx_params = lm_context->tx_params_override;
else
lm_context->serdes_obj->tx_advanced_params_get(
lm_context->serdes_obj,
lm_context->lane,
tx_params);
}
if (rx_params != NULL) {
if (lm_context->serdes_rx_params_valid)
*rx_params = lm_context->rx_params_override;
else
lm_context->serdes_obj->rx_advanced_params_get(
lm_context->serdes_obj,
lm_context->lane,
rx_params);
}
return (0);
}
const char *
al_eth_lm_mode_convert_to_str(enum al_eth_lm_link_mode val)
{
switch (val) {
case AL_ETH_LM_MODE_DISCONNECTED:
return ("AL_ETH_LM_MODE_DISCONNECTED");
case AL_ETH_LM_MODE_10G_OPTIC:
return ("AL_ETH_LM_MODE_10G_OPTIC");
case AL_ETH_LM_MODE_10G_DA:
return ("AL_ETH_LM_MODE_10G_DA");
case AL_ETH_LM_MODE_1G:
return ("AL_ETH_LM_MODE_1G");
case AL_ETH_LM_MODE_25G:
return ("AL_ETH_LM_MODE_25G");
}
return ("N/A");
}
void
al_eth_lm_debug_mode_set(struct al_eth_lm_context *lm_context,
boolean_t enable)
{
lm_context->debug = enable;
}