diff options
Diffstat (limited to 'sys/contrib/alpine-hal/al_hal_serdes_25g.c')
-rw-r--r-- | sys/contrib/alpine-hal/al_hal_serdes_25g.c | 1951 |
1 files changed, 1951 insertions, 0 deletions
diff --git a/sys/contrib/alpine-hal/al_hal_serdes_25g.c b/sys/contrib/alpine-hal/al_hal_serdes_25g.c new file mode 100644 index 000000000000..68767e557fd1 --- /dev/null +++ b/sys/contrib/alpine-hal/al_hal_serdes_25g.c @@ -0,0 +1,1951 @@ +/******************************************************************************* +Copyright (C) 2015 Annapurna Labs Ltd. + +This file may be licensed under the terms of the Annapurna Labs Commercial +License Agreement. + +Alternatively, this file can be distributed under the terms of the GNU General +Public License V2 as published by the Free Software Foundation and can be +found at http://www.gnu.org/licenses/gpl-2.0.html + +Alternatively, redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "al_hal_serdes_25g.h" +#include "al_hal_serdes_25g_regs.h" +#include "al_hal_serdes_25g_internal_regs.h" + +#define AL_SERDES_MB_MAX_DATA_LEN 8 + +#define AL_SERDES_25G_WAIT_FOR_READY_TO 200 +#define AL_SERDES_25G_RESET_TO 100 +#define AL_SERDES_25G_RESET_NUM_RETRIES 5 + +#if (!defined(AL_SERDES_BASIC_SERVICES_ONLY)) || (AL_SERDES_BASIC_SERVICES_ONLY == 0) +#define AL_SRDS_ADV_SRVC(func) func +#else +static void al_serdes_hssp_stub_func(void) +{ + al_err("%s: not implemented service called!\n", __func__); +} + +#define AL_SRDS_ADV_SRVC(func) ((typeof(func) *)al_serdes_hssp_stub_func) +#endif + +/******************************************************************************/ +/******************************************************************************/ +static enum al_serdes_type al_serdes_25g_type_get(void) +{ + return AL_SRDS_TYPE_25G; +} + +/******************************************************************************/ +/******************************************************************************/ +static int al_serdes_25g_reg_read( + struct al_serdes_grp_obj *obj, + enum al_serdes_reg_page page, + enum al_serdes_reg_type type, + uint16_t offset, + uint8_t *data) +{ + struct al_serdes_c_regs __iomem *regs_base = obj->regs_base; + uint32_t addr = 0; + + al_dbg("%s(%p, %d, %d, %u)\n", __func__, obj, page, type, offset); + + al_assert(obj); + al_assert(data); + + switch (page) { + case AL_SRDS_REG_PAGE_TOP: + addr = (SERDES_25G_TOP_BASE + offset); + break; + case AL_SRDS_REG_PAGE_4_COMMON: + addr = (SERDES_25G_CM_BASE + offset); + break; + case AL_SRDS_REG_PAGE_0_LANE_0: + case AL_SRDS_REG_PAGE_1_LANE_1: + addr = (SERDES_25G_LANE_BASE + (page * SERDES_25G_LANE_SIZE) + offset); + break; + default: + al_err("%s: wrong serdes type %d\n", __func__, type); + return -1; + } + + al_reg_write32(®s_base->gen.reg_addr, addr); + *data = al_reg_read32(®s_base->gen.reg_data); + + al_dbg("%s: return(%u)\n", __func__, *data); + + return 0; +} + +static int al_serdes_25g_reg_write( + struct al_serdes_grp_obj *obj, + enum al_serdes_reg_page page, + enum al_serdes_reg_type type, + uint16_t offset, + uint8_t data) +{ + struct al_serdes_c_regs __iomem *regs_base = obj->regs_base; + uint32_t addr = 0; + + al_dbg("%s(%p, %d, %d, %u)\n", __func__, obj, page, type, offset); + + al_assert(obj); + + switch (page) { + case AL_SRDS_REG_PAGE_TOP: + addr = (SERDES_25G_TOP_BASE + offset); + break; + case AL_SRDS_REG_PAGE_4_COMMON: + addr = (SERDES_25G_CM_BASE + offset); + break; + case AL_SRDS_REG_PAGE_0_LANE_0: + case AL_SRDS_REG_PAGE_1_LANE_1: + addr = (SERDES_25G_LANE_BASE + (page * SERDES_25G_LANE_SIZE) + offset); + break; + default: + al_err("%s: wrong serdes type %d\n", __func__, type); + return -1; + } + + al_reg_write32(®s_base->gen.reg_addr, addr); + al_reg_write32(®s_base->gen.reg_data, (data | SERDES_C_GEN_REG_DATA_STRB_MASK)); + + al_dbg("%s: write(%u)\n", __func__, data); + + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +static int al_serdes_25g_reg_masked_read( + struct al_serdes_grp_obj *obj, + enum al_serdes_reg_page page, + uint16_t offset, + uint8_t mask, + uint8_t shift, + uint8_t *data) +{ + uint8_t val; + int status = 0; + + status = al_serdes_25g_reg_read(obj, page, 0, offset, &val); + if (status) + return status; + + *data = AL_REG_FIELD_GET(val, mask, shift); + + return 0; +} + +static int al_serdes_25g_reg_masked_write( + struct al_serdes_grp_obj *obj, + enum al_serdes_reg_page page, + uint16_t offset, + uint8_t mask, + uint8_t shift, + uint8_t data) +{ + uint8_t val; + int status = 0; + + status = al_serdes_25g_reg_read(obj, page, 0, offset, &val); + if (status) + return status; + + val &= (~mask); + val |= (data << shift); + return al_serdes_25g_reg_write(obj, page, 0, offset, val); +} + +/******************************************************************************/ +/******************************************************************************/ +#define SERDES_25G_MB_RESP_BYTES 16 +#define SERDES_25G_MB_TIMEOUT 5000000 /* uSec */ + +static int al_serdes_25g_mailbox_send_cmd( + struct al_serdes_grp_obj *obj, + uint8_t cmd, + uint8_t *data, + uint8_t data_len) +{ + uint8_t val; + int i; + uint32_t timeout = SERDES_25G_MB_TIMEOUT; + + if (data_len > AL_SERDES_MB_MAX_DATA_LEN) { + al_err("Cannot send command, data too long\n"); + return -1; + } + + /* Wait for CMD_FLAG to clear */ + while(1) { + al_serdes_25g_reg_read(obj, AL_SRDS_REG_PAGE_TOP, 0, + SERDES_25G_TOP_CMD_FLAG_ADDR, &val); + if (val == 0) + break; + + if (timeout == 0) { + al_err("%s: timeout occurred waiting to CMD_FLAG\n", __func__); + return -1; + } + + timeout--; + al_udelay(1); + } + + for (i = 0; i < data_len; i++) { + al_serdes_25g_reg_write(obj, AL_SRDS_REG_PAGE_TOP, 0, + (SERDES_25G_TOP_CMD_DATA0_ADDR + i), data[i]); + } + + /* this write will set CMD_FLAG automatically */ + al_serdes_25g_reg_write(obj, AL_SRDS_REG_PAGE_TOP, 0, SERDES_25G_TOP_CMD_ADDR, cmd); + + return 0; +} + +static int al_serdes_25g_mailbox_recv_rsp( + struct al_serdes_grp_obj *obj, + uint8_t *rsp_code, + uint8_t *data, + uint8_t *data_len) +{ + uint8_t val; + int i; + uint32_t timeout = SERDES_25G_MB_TIMEOUT; + + /* wait for RSP_FLAG to set */ + while(1) { + al_serdes_25g_reg_read(obj, AL_SRDS_REG_PAGE_TOP, 0, + SERDES_25G_TOP_RSP_FLAG_ADDR, &val); + if (val == 0x1) + break; + + if (timeout == 0) { + al_err("%s: timeout occurred waiting to RSP_FLAG\n", __func__); + *data_len = 0; + return -1; + } + + timeout--; + al_udelay(1); + } + + /* Grab the response code and data */ + al_serdes_25g_reg_read(obj, AL_SRDS_REG_PAGE_TOP, 0, + SERDES_25G_TOP_RSP_ADDR, rsp_code); + + for (i = 0; i < SERDES_25G_MB_RESP_BYTES; i++) { + al_serdes_25g_reg_read(obj, AL_SRDS_REG_PAGE_TOP, 0, + (SERDES_25G_TOP_RSP_DATA0_ADDR + i), &data[i]); + } + + /* clear the RSP_FLAG (write 1 to clear) */ + al_serdes_25g_reg_write(obj, AL_SRDS_REG_PAGE_TOP, 0, + SERDES_25G_TOP_RSP_FLAG_ADDR, 0x1); + + *data_len = SERDES_25G_MB_RESP_BYTES; + + return 0; +} + +/******************************************************************************/ +/******************************************************************************/ +static void al_serdes_25g_bist_rx_enable( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + al_bool enable) +{ + if (enable) { + switch (lane) { + case 0: + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_ADDR, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_CTRL_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_CTRL_CG_EN_SHIFT, + 0x1); + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_ADDR, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_CTRL_BIST_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN0_CLK_RX_CTRL_BIST_CG_EN_SHIFT, + 0x1); + break; + case 1: + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_ADDR, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_CTRL_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_CTRL_CG_EN_SHIFT, + 0x1); + + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_ADDR, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_CTRL_BIST_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN1_CLK_RX_CTRL_BIST_CG_EN_SHIFT, + 0x1); + break; + default: + al_err("%s: Wrong serdes lane %d\n", __func__, lane); + return; + } + + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_LOSS_LOCK_CTRL4_ADDR, + SERDES_25G_LANE_RX_BIST_LOSS_LOCK_CTRL4_STOP_ON_LOSS_LOCK_MASK, + SERDES_25G_LANE_RX_BIST_LOSS_LOCK_CTRL4_STOP_ON_LOSS_LOCK_SHIFT, + 0); + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_CTRL_ADDR, + SERDES_25G_LANE_RX_BIST_CTRL_EN_MASK, + SERDES_25G_LANE_RX_BIST_CTRL_EN_SHIFT, + 1); + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_CTRL_ADDR, + SERDES_25G_LANE_RX_BIST_CTRL_PATTERN_SEL_MASK, + SERDES_25G_LANE_RX_BIST_CTRL_PATTERN_SEL_SHIFT, + 6); + } else { + /* clear counters */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_CTRL_ADDR, + SERDES_25G_LANE_RX_BIST_CTRL_CLEAR_BER_MASK, + SERDES_25G_LANE_RX_BIST_CTRL_CLEAR_BER_SHIFT, + 1); + + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_CTRL_ADDR, + SERDES_25G_LANE_RX_BIST_CTRL_CLEAR_BER_MASK, + SERDES_25G_LANE_RX_BIST_CTRL_CLEAR_BER_SHIFT, + 0); + + al_msleep(AL_SERDES_25G_WAIT_FOR_READY_TO); + + /* disable */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_CTRL_ADDR, + SERDES_25G_LANE_RX_BIST_CTRL_EN_MASK, + SERDES_25G_LANE_RX_BIST_CTRL_EN_SHIFT, + 0); + } +} + +// TODO: [Guy] change API to be per lane. +static void al_serdes_25g_bist_pattern_select( + struct al_serdes_grp_obj *obj, + enum al_serdes_bist_pattern pattern, + uint8_t *user_data) +{ + enum al_serdes_lane lane; + uint8_t val = 0; + + switch (pattern) { + case AL_SRDS_BIST_PATTERN_USER: + al_assert(user_data); + val = SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_PRBS_USER; + break; + case AL_SRDS_BIST_PATTERN_PRBS7: + val = SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_PRBS7; + break; + case AL_SRDS_BIST_PATTERN_PRBS23: + val = SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_PRBS23; + break; + case AL_SRDS_BIST_PATTERN_PRBS31: + val = SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_PRBS31; + break; + case AL_SRDS_BIST_PATTERN_CLK1010: + default: + al_err("%s: invalid pattern (%d)\n", __func__, pattern); + al_assert(0); + } + + for (lane = AL_SRDS_LANE_0; lane <= AL_SRDS_LANE_1; lane++) { + if (pattern == AL_SRDS_BIST_PATTERN_USER) { + int i; + + for (i = 0; i < SERDES_25G_LANE_TX_BIST_UDP_NUM_BYTES; i++) + al_serdes_25g_reg_write( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_TX_BIST_UDP_ADDR(i), + user_data[i]); + } + + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TX_BIST_CTRL_ADDR, + SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_SEL_MASK, + SERDES_25G_LANE_TX_BIST_CTRL_PATTERN_SEL_SHIFT, + val); + } +} + +static void al_serdes_25g_bist_tx_enable( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + al_bool enable) +{ + if (enable) { + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TX_BIST_CTRL_ADDR, + SERDES_25G_LANE_TX_BIST_CTRL_EN_MASK, + SERDES_25G_LANE_TX_BIST_CTRL_EN_SHIFT, + 0x1); + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TOP_DPL_TXDP_CTRL1_ADDR, + SERDES_25G_LANE_TOP_DPL_TXDP_CTRL1_DMUX_TXA_SEL_MASK, + SERDES_25G_LANE_TOP_DPL_TXDP_CTRL1_DMUX_TXA_SEL_SHIFT, + 0x2); + + switch (lane) { + case AL_SRDS_LANE_0: + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN0_CLK_TX_ADDR, + SERDES_25G_TOP_CLOCK_LN0_CLK_TX_CTRL_BIST_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN0_CLK_TX_CTRL_BIST_CG_EN_SHIFT, + 0x1); + break; + case AL_SRDS_LANE_1: + al_serdes_25g_reg_masked_write( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_CLOCK_LN1_CLK_TX_ADDR, + SERDES_25G_TOP_CLOCK_LN1_CLK_TX_CTRL_BIST_CG_EN_MASK, + SERDES_25G_TOP_CLOCK_LN1_CLK_TX_CTRL_BIST_CG_EN_SHIFT, + 0x1); + break; + default: + al_err("%s: Wrong serdes lane %d\n", __func__, lane); + return; + } + } else { + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TX_BIST_CTRL_ADDR, + SERDES_25G_LANE_TX_BIST_CTRL_EN_MASK, + SERDES_25G_LANE_TX_BIST_CTRL_EN_SHIFT, + 0); + } + +} + +static void al_serdes_25g_bist_rx_status( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + al_bool *is_locked, + al_bool *err_cnt_overflow, + uint32_t *err_cnt) +{ + uint8_t status; + uint8_t err1; + uint8_t err2; + uint8_t err3; + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_STATUS_ADDR, + SERDES_25G_LANE_RX_BIST_STATUS_STATE_MASK, + SERDES_25G_LANE_RX_BIST_STATUS_STATE_SHIFT, + &status); + + if (status != 3) { + *is_locked = AL_FALSE; + return; + } + + *is_locked = AL_TRUE; + *err_cnt_overflow = AL_FALSE; + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_BER_STATUS0_ADDR, + SERDES_25G_LANE_RX_BIST_BER_STATUS0_BIT_ERROR_COUNT_7_0_MASK, + SERDES_25G_LANE_RX_BIST_BER_STATUS0_BIT_ERROR_COUNT_7_0_SHIFT, + &err1); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_BER_STATUS1_ADDR, + SERDES_25G_LANE_RX_BIST_BER_STATUS1_BIT_ERROR_COUNT_15_8_MASK, + SERDES_25G_LANE_RX_BIST_BER_STATUS1_BIT_ERROR_COUNT_15_8_SHIFT, + &err2); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_RX_BIST_BER_STATUS2_ADDR, + SERDES_25G_LANE_RX_BIST_BER_STATUS2_BIT_ERROR_COUNT_23_16_MASK, + SERDES_25G_LANE_RX_BIST_BER_STATUS2_BIT_ERROR_COUNT_23_16_SHIFT, + &err3); + + *err_cnt = (err1 + (err2 << 8) + (err3 << 16)); +} + +#define SERDES_MB_CMD_SWING_CFG 0x83 +#define SERDES_MB_CMD_SAMPLES_COUNT 0x84 +#define SERDES_MB_CMD_START_MEASURE 0x82 + +#define SERDES_MB_RSP_CODE_0 0 +#define SERDES_MB_RSP_CODE_1 1 +#define SERDES_MB_RSP_CODE_2 2 + +static int al_serdes_25g_eye_diag_run( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + int x_start, + int x_stop, + unsigned int x_step, + int y_start, + int y_stop, + unsigned int y_step, + uint64_t ber_target, + uint64_t *buf, + uint32_t buf_size) +{ + int rc; + uint8_t rsp_code; + uint8_t data[16]; + uint8_t data_len; + uint32_t total_bits; + uint8_t bits_left_curr_sample; + uint8_t bits_left_curr_byte; + uint32_t byte = 0; + uint32_t x = 0; + uint32_t x_samples = (((x_stop - x_start) / x_step) + 1); + uint32_t y = 0; + uint32_t y_samples = (((y_stop - y_start) / y_step) + 1); + uint8_t sample_width = (64 - __builtin_clzl(ber_target)); + uint8_t msb; + uint8_t lsb; + uint32_t samples_left = ((x_samples * y_samples)); + uint8_t sign = 0; + + al_assert(buf_size == (samples_left * sizeof(uint64_t))); + + al_memset(buf, 0, buf_size); + + if (y_start < 0) { + y_start *= -1; + sign |= 0x1; + } + + if (y_stop < 0) { + y_stop *= -1; + sign |= 0x2; + } + + data[0] = lane; + data[1] = x_start; + data[2] = x_stop; + data[3] = x_step; + data[4] = y_start; + data[5] = y_stop; + data[6] = sign; + data[7] = y_step; + + rc = al_serdes_25g_mailbox_send_cmd( + obj, + SERDES_MB_CMD_SWING_CFG, + data, + 8); + + if (rc) { + al_err("%s: Failed to send command %d to mailbox.\n", + __func__, SERDES_MB_CMD_SWING_CFG); + return rc; + } + + rc = al_serdes_25g_mailbox_recv_rsp( + obj, + &rsp_code, + data, + &data_len); + + if ((rc) || (rsp_code != SERDES_MB_RSP_CODE_0)) { + al_err("%s: Failed to send command %d to mailbox. rsp_code %d\n", + __func__, SERDES_MB_CMD_SWING_CFG, rsp_code); + + return (ETIMEDOUT); + } + + al_assert(sample_width <= 40); + + data[0] = lane; + data[1] = ((ber_target >> 32) & 0xFF); + data[2] = ((ber_target >> 24) & 0xFF); + data[3] = ((ber_target >> 16) & 0xFF); + data[4] = ((ber_target >> 8) & 0xFF); + data[5] = (ber_target & 0xFF); + + rc = al_serdes_25g_mailbox_send_cmd( + obj, + SERDES_MB_CMD_SAMPLES_COUNT, + data, + 6); + + if (rc) { + al_err("%s: Failed to send command %d to mailbox.\n", + __func__, SERDES_MB_CMD_SAMPLES_COUNT); + return rc; + } + + rc = al_serdes_25g_mailbox_recv_rsp( + obj, + &rsp_code, + data, + &data_len); + + if ((rc) || (rsp_code != SERDES_MB_RSP_CODE_0)) { + al_err("%s: Failed to send command %d to mailbox. rsp_code %d\n", + __func__, SERDES_MB_CMD_SAMPLES_COUNT, rsp_code); + + return (ETIMEDOUT); + } + + rc = al_serdes_25g_mailbox_send_cmd( + obj, + SERDES_MB_CMD_START_MEASURE, + data, + 0); + + bits_left_curr_sample = sample_width; + + while (rsp_code != SERDES_MB_RSP_CODE_1) { + uint8_t num_bits = 0; + + rc = al_serdes_25g_mailbox_recv_rsp( + obj, + &rsp_code, + data, + &data_len); + + if ((rc != 0) || (rsp_code > SERDES_MB_RSP_CODE_2)) { + al_err("%s: command %d return failure. rsp_code %d\n", + __func__, SERDES_MB_CMD_START_MEASURE, rsp_code); + + return (ETIMEDOUT); + } + byte = 0; + total_bits = data_len * 8; + bits_left_curr_byte = 8; + while (total_bits > 0) { + num_bits = al_min_t(uint8_t, bits_left_curr_sample, bits_left_curr_byte); + + buf[(y * x_samples) + x] <<= num_bits; + msb = bits_left_curr_byte - 1; + lsb = msb - num_bits + 1; + buf[(y * x_samples) + x] |= (data[byte] & AL_FIELD_MASK(msb, lsb) >> lsb); + + total_bits -= num_bits; + + bits_left_curr_byte -= num_bits; + if (!bits_left_curr_byte) { + bits_left_curr_byte = 8; + byte++; + } + + bits_left_curr_sample -= num_bits; + if (!bits_left_curr_sample) { + y++; + if (y == y_samples) { + y = 0; + x++; + } + + samples_left--; + bits_left_curr_sample = sample_width; + } + + if (samples_left == 0) + break; + } + + if ((samples_left == 0) && (rsp_code != SERDES_MB_RSP_CODE_1)) { + rc = al_serdes_25g_mailbox_recv_rsp( + obj, + &rsp_code, + data, + &data_len); + if ((rc) || (rsp_code == SERDES_MB_RSP_CODE_0)) { + al_err("%s: Parsed enough samples but f/w is still sending more\n", + __func__); + + return -EIO; + } + break; + } + } + + if (samples_left > 0) { + al_err("%s: Still need more samples but f/w has stopped sending them!?!?!?\n", + __func__); + + return -EIO; + } + + return 0; +} + +#define SERDES_25G_EYE_X_MIN 1 +#define SERDES_25G_EYE_X_MAX 127 +#define SERDES_25G_EYE_Y_MIN -200 +#define SERDES_25G_EYE_Y_MAX 200 +#define SERDES_25G_EYE_SIZE_MAX_SAMPLES 401 +#define SERDES_25G_EYE_SIZE_BER_TARGET 0xffff +#define SERDES_25G_EYE_SIZE_ERR_TH 10 + +static int al_serdes_25g_calc_eye_size( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + int *width, + int *height) +{ + uint64_t samples[SERDES_25G_EYE_SIZE_MAX_SAMPLES]; + int i; + int _width = 0; + int _height = 0; + int rc; + int mid_x = ((SERDES_25G_EYE_X_MIN + SERDES_25G_EYE_X_MAX) / 2); + int mid_y = ((SERDES_25G_EYE_Y_MIN + SERDES_25G_EYE_Y_MAX) / 2); + + *height = 0; + *width = 0; + + rc = al_serdes_25g_eye_diag_run(obj, + lane, + mid_x, + mid_x, + 1, + SERDES_25G_EYE_Y_MIN, + SERDES_25G_EYE_Y_MAX, + 1, + SERDES_25G_EYE_SIZE_BER_TARGET, + samples, + ((SERDES_25G_EYE_Y_MAX - SERDES_25G_EYE_Y_MIN + 1) * + sizeof(uint64_t))); + + if (rc) { + al_err("%s: failed to run eye_diag\n", __func__); + return rc; + } + + for (i = (mid_y - SERDES_25G_EYE_Y_MIN); + ((samples[i] < SERDES_25G_EYE_SIZE_ERR_TH) && + (i < (SERDES_25G_EYE_Y_MAX - SERDES_25G_EYE_Y_MIN + 1))); + i++, (_height)++) + ; + for (i = (mid_y - SERDES_25G_EYE_Y_MIN); + ((samples[i] < SERDES_25G_EYE_SIZE_ERR_TH) && (i >= 0)); + i--, (_height)++) + ; + + rc = al_serdes_25g_eye_diag_run(obj, + lane, + SERDES_25G_EYE_X_MIN, + SERDES_25G_EYE_X_MAX, + 1, + mid_y, + mid_y, + 1, + SERDES_25G_EYE_SIZE_BER_TARGET, + samples, + ((SERDES_25G_EYE_X_MAX - SERDES_25G_EYE_X_MIN + 1) * + sizeof(uint64_t))); + + if (rc) { + al_err("%s: failed to run eye_diag\n", __func__); + return rc; + } + + for (i = (mid_x - SERDES_25G_EYE_X_MIN); + ((samples[i] < SERDES_25G_EYE_SIZE_ERR_TH) && + (i < (SERDES_25G_EYE_X_MAX - SERDES_25G_EYE_X_MIN + 1))); + i++, (_width)++) + ; + for (i = (mid_x - SERDES_25G_EYE_X_MIN); + ((samples[i] < SERDES_25G_EYE_SIZE_ERR_TH) && (i >= 0)); + i--, (_width)++) + ; + + *height = _height; + *width = _width; + + return 0; +} + + +static void al_serdes_25g_tx_advanced_params_set(struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + void *tx_params) +{ + struct al_serdes_adv_tx_params *params = tx_params; + uint32_t timeout = 5000; + uint8_t val = 0; + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_TXEQ_CM1_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_TXEQ_CM1_SHIFT, + params->c_minus_1); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_TXEQ_C1_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_TXEQ_C1_SHIFT, + params->c_plus_1); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_DRV_SWING_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_DRV_SWING_SHIFT, + params->total_driver_units); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_REQ_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_REQ_SHIFT, + 1); + + + /* wait for acknowledge */ + while (1) { + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_STATUS0_ADDR, + SERDES_25G_LANE_DRV_TXEQ_STATUS0_ACK_MASK, + SERDES_25G_LANE_DRV_TXEQ_STATUS0_ACK_SHIFT, + &val); + if (val == 1) + break; + + if (timeout == 0) { + al_err("%s: timeout occurred waiting to FW ack\n", __func__); + break; + } + + timeout--; + al_udelay(1); + } + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_REQ_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL0_REQ_SHIFT, + 0); +} + +static void al_serdes_25g_tx_advanced_params_get(struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + void *tx_params) +{ + struct al_serdes_adv_tx_params *params = tx_params; + + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_TXEQ_CM1_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL3_TXEQ_CM1_SHIFT, + ¶ms->c_minus_1); + + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_TXEQ_C1_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL1_TXEQ_C1_SHIFT, + ¶ms->c_plus_1); + + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_ADDR, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_DRV_SWING_MASK, + SERDES_25G_LANE_DRV_TXEQ_CTRL5_DRV_SWING_SHIFT, + ¶ms->total_driver_units); +} + +static al_bool al_serdes_25g_cdr_is_locked( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane) +{ + uint8_t reg; + + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_CDR_RXCLK_DLPF_STATUS5_ADDR, + SERDES_25G_LANE_CDR_RXCLK_DLPF_STATUS5_LOCKED_MASK, + SERDES_25G_LANE_CDR_RXCLK_DLPF_STATUS5_LOCKED_SHIFT, + ®); + + return !!reg; + +} + +static al_bool al_serdes_25g_rx_valid( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane) +{ + uint8_t reg; + + al_serdes_25g_reg_masked_read(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TOP_LN_STAT_CTRL0_ADDR, + SERDES_25G_LANE_TOP_LN_STAT_CTRL0_RXVALID_MASK, + SERDES_25G_LANE_TOP_LN_STAT_CTRL0_RXVALID_SHIFT, + ®); + + return !!reg; + +} + +static al_bool al_serdes_25g_signal_is_detected( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane) +{ + struct al_serdes_c_regs __iomem *regs_base = obj->regs_base; + uint32_t reg; + al_bool signal_detect = AL_FALSE; + + reg = al_reg_read32(®s_base->lane[lane].stat); + + signal_detect = ((reg & (SERDES_C_LANE_STAT_LN_STAT_LOS | + SERDES_C_LANE_STAT_LN_STAT_LOS_DEGLITCH)) ? + AL_FALSE : AL_TRUE); + + return signal_detect; + +} + +static int al_serdes_25g_rx_equalization( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane) +{ + struct al_serdes_c_regs __iomem *regs_base = obj->regs_base; + uint32_t ready_mask = (SERDES_C_GEN_STATUS_CM0_RST_PD_READY | SERDES_C_GEN_STATUS_CM0_OK_O); + uint32_t reset_mask; + uint32_t timeout; + uint32_t reg_val; + uint32_t retries = AL_SERDES_25G_RESET_NUM_RETRIES; + int status = 0; + + if (lane == 0) { + ready_mask |= SERDES_C_GEN_STATUS_LN0_RST_PD_READY; + reset_mask = SERDES_C_GEN_RST_LN0_RST_N; + } else { + ready_mask |= SERDES_C_GEN_STATUS_LN1_RST_PD_READY; + reset_mask = SERDES_C_GEN_RST_LN1_RST_N; + } + + while (retries > 0) { + timeout = AL_SERDES_25G_WAIT_FOR_READY_TO; + status = 0; + + al_reg_write32_masked(®s_base->gen.rst, reset_mask, 0); + + al_msleep(AL_SERDES_25G_RESET_TO); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_FEATURE_CTLE_ADAPT_MBS_CFG_ADDR, + SERDES_25G_LANE_FEATURE_CTLE_ADAPT_MBS_CFG_INIT0_EN_MASK, + SERDES_25G_LANE_FEATURE_CTLE_ADAPT_MBS_CFG_INIT0_EN_SHIFT, + 0); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_EQ_MBF_START_MASK, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_EQ_MBF_START_SHIFT, + 7); + + al_serdes_25g_reg_masked_write(obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_EQ_MBG_START_MASK, + SERDES_25G_LANE_LEQ_REFCLK_EQ_MB_CTRL1_EQ_MBG_START_SHIFT, + 15); + + al_msleep(AL_SERDES_25G_RESET_TO); + + al_reg_write32_masked(®s_base->gen.rst, reset_mask, reset_mask); + + while (1) { + reg_val = al_reg_read32(®s_base->gen.status); + if ((reg_val & ready_mask) == ready_mask) + break; + + al_udelay(1); + timeout--; + + if (timeout == 0) { + al_err("%s: Timeout waiting for serdes ready\n", __func__); + status = ETIMEDOUT; + retries--; + break; + } + } + + if (status) + continue; + + while (1) { + reg_val = al_reg_read32(®s_base->lane[lane].stat); + reg_val &= (SERDES_C_LANE_STAT_LNX_STAT_OK | + SERDES_C_LANE_STAT_LN_STAT_RXVALID); + if (reg_val == (SERDES_C_LANE_STAT_LNX_STAT_OK | + SERDES_C_LANE_STAT_LN_STAT_RXVALID)) + break; + + al_udelay(1); + timeout--; + + if (timeout == 0) { + al_err("%s: TO waiting for lane ready (%x)\n", __func__, reg_val); + status = ETIMEDOUT; + retries--; + break; + } + } + + if (status) + continue; + + break; + } + + if (retries == 0) { + al_err("%s: Failed to run equalization\n", __func__); + status = ETIMEDOUT; + } + + return status; + +} + +#define AL_SERDES_25G_GCFSM2_READ_TIMEOUT 2000000 /* uSec */ + +static int al_serdes_25g_gcfsm2_read( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + uint8_t offset, + uint16_t *data) +{ + int status = 0; + uint32_t timeout = AL_SERDES_25G_GCFSM2_READ_TIMEOUT; + uint8_t ack = 0; + uint8_t data_low, data_high; + + al_assert(data); + + /* Make sure GCFSM2 REQuest is off */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_ADDR, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_MASK, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_SHIFT, + 0); + /* Write GCFSM2 CMD; CMD=0 for Read Request */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_CMD_CTRL1_ADDR, + SERDES_25G_LANE_GCFSM2_CMD_CTRL1_CMD_MASK, + SERDES_25G_LANE_GCFSM2_CMD_CTRL1_CMD_SHIFT, + 0); + /* Write GCFSM2 the Address we wish to read */ + al_serdes_25g_reg_write( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_GCFSM2_CMD_CTRL2_ADDR, + offset); + /* Issue a command REQuest */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_ADDR, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_MASK, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_SHIFT, + 1); + /* Poll on GCFSM2 ACK */ + while (1) { + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_CMD_STATUS_ADDR, + SERDES_25G_LANE_GCFSM2_CMD_STATUS_ACK_MASK, + SERDES_25G_LANE_GCFSM2_CMD_STATUS_ACK_SHIFT, + &ack); + + if (ack || (timeout == 0)) + break; + + timeout--; + al_udelay(1); + } + + if (ack) { + /* Read 12bit of register value */ + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_GCFSM2_READ_SHADOW_DATA_STATUS0_ADDR, + &data_low); + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_READ_SHADOW_DATA_STATUS1_ADDR, + SERDES_25G_LANE_GCFSM2_READ_SHADOW_DATA_STATUS1_11_8_MASK, + SERDES_25G_LANE_GCFSM2_READ_SHADOW_DATA_STATUS1_11_8_SHIFT, + &data_high); + *data = (data_high << 8) | data_low; + } else { + al_err("%s: TO waiting for GCFSM2 req to complete (%x)\n", __func__, offset); + status = ETIMEDOUT; + } + + /* Deassert the GCFSM2 REQuest */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_ADDR, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_MASK, + SERDES_25G_LANE_GCFSM2_CMD_CTRL0_REQ_SHIFT, + 0); + + return status; +} + +enum al_serdes_25g_rx_leq_fsm_opcode { + AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ = 0x1, + AL_SERDES_25G_RX_LEQ_FSM_OPCODE_WRITE = 0x2, +}; + +enum al_serdes_25g_rx_leq_fsm_target { + AL_SERDES_25G_RX_LEQ_FSM_TARGET_AGC_SOURCE = 0x1, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_PLE_ATT = 0x2, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_LFG = 0x3, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_GN_APG = 0x4, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_GNEQ_CCL_LFG = 0x5, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_HFG_SQL = 0x6, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_MBF = 0x8, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_MBG = 0x9, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_VSCAN = 0xA, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_HSCAN = 0xB, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EYE_INTF = 0xC, +}; + +#define AL_SERDES_25G_RX_LEQ_FSM_TIMEOUT 2000000 /* uSec */ + +static int al_serdes_25g_rx_leq_fsm_op( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + enum al_serdes_25g_rx_leq_fsm_opcode opcode, + enum al_serdes_25g_rx_leq_fsm_target target, + uint8_t val, + uint8_t *data, + uint8_t *err) +{ + uint32_t reg; + uint32_t timeout = AL_SERDES_25G_RX_LEQ_FSM_TIMEOUT; + uint8_t ack = 0; + int status = 0; + + al_assert(data); + al_assert(err); + + /* Write the OpCode & Target to LEQ FSM */ + reg = (target << 4) | opcode; + al_serdes_25g_reg_write( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CMD0_ADDR, + reg); + + /* Write 0 as MiscOption value to LEQ FSM */ + al_serdes_25g_reg_write( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CMD2_ADDR, + 0); + + /* Write the ArgumentValue to LEQ FSM if needed*/ + if (opcode == AL_SERDES_25G_RX_LEQ_FSM_OPCODE_WRITE) { + al_serdes_25g_reg_write( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CMD1_ADDR, + val); + } + + /* Issue an LEQ FSM Command Request */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_LEQ_FSM_CMD_REQ_MASK, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_LEQ_FSM_CMD_REQ_SHIFT, + 1); + + /* Poll on LEQ FSM Command acknowledge */ + while (1) { + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS5_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS5_LEQ_FSM_CMD_ACK_MASK, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS5_LEQ_FSM_CMD_ACK_SHIFT, + &ack); + + if (ack || (timeout == 0)) + break; + + timeout--; + al_udelay(1); + } + + if (ack) { + uint8_t err1, err2; + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS0_ADDR, + err); + + err1 = (*err & + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS0_LEQ_FSM_STATUS_ERROR1_MASK) >> + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS0_LEQ_FSM_STATUS_ERROR1_SHIFT; + err2 = (*err & + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS0_LEQ_FSM_STATUS_ERROR2_MASK) >> + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS0_LEQ_FSM_STATUS_ERROR2_SHIFT; + + if (err1 || err2) { + al_err("%s: error in RX LEQ FSM req, err status 1=0x%x, err status 2=0x%x", + __func__, err1, err2); + status = -EIO; + } + + /* Read LEQ FSM Command return Value */ + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_STATUS3_ADDR, + data); + + /* Clear an LEQ FSM Command Request */ + al_serdes_25g_reg_masked_write( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_LEQ_FSM_CMD_REQ_MASK, + SERDES_25G_LANE_LEQ_REFCLK_LEQ_FSM_CTRL0_LEQ_FSM_CMD_REQ_SHIFT, + 0); + } else { + al_err("%s: TO waiting for RX LEQ FSM req to complete (opcode %x, target %x, val %x)\n", + __func__, opcode, target, val); + status = ETIMEDOUT; + } + + return status; +} + +/* enum values correspond to HW values, don't change! */ +enum al_serdes_25g_tbus_obj { + AL_SERDES_25G_TBUS_OBJ_TOP = 0, + AL_SERDES_25G_TBUS_OBJ_CMU = 1, + AL_SERDES_25G_TBUS_OBJ_LANE = 2, +}; + +#define AL_SERDES_25G_TBUS_DELAY 1000 /* uSec */ +#define AL_SERDES_25G_TBUS_ADDR_HIGH_SHIFT 5 + +static int al_serdes_25g_tbus_read( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + enum al_serdes_25g_tbus_obj tbus_obj, + uint8_t offset, + uint16_t *data) +{ + uint8_t addr_high, val_high, val_low; + + al_assert(lane < AL_SRDS_NUM_LANES); + + if (tbus_obj == AL_SERDES_25G_TBUS_OBJ_TOP) + addr_high = AL_SERDES_25G_TBUS_OBJ_TOP; + else if (tbus_obj == AL_SERDES_25G_TBUS_OBJ_CMU) + addr_high = AL_SERDES_25G_TBUS_OBJ_CMU; + else + addr_high = AL_SERDES_25G_TBUS_OBJ_LANE + lane; + + addr_high <<= AL_SERDES_25G_TBUS_ADDR_HIGH_SHIFT; + + al_serdes_25g_reg_write( + obj, + AL_SRDS_REG_PAGE_TOP, + 0, + SERDES_25G_TOP_TBUS_ADDR_7_0_ADDR, + offset); + + al_serdes_25g_reg_write( + obj, + AL_SRDS_REG_PAGE_TOP, + 0, + SERDES_25G_TOP_TBUS_ADDR_15_8_ADDR, + addr_high); + + al_udelay(AL_SERDES_25G_TBUS_DELAY); + + al_serdes_25g_reg_read( + obj, + AL_SRDS_REG_PAGE_TOP, + 0, + SERDES_25G_TOP_TBUS_DATA_7_0_ADDR, + &val_low); + + al_serdes_25g_reg_masked_read( + obj, + AL_SRDS_REG_PAGE_TOP, + SERDES_25G_TOP_TBUS_DATA_11_8_ADDR, + SERDES_25G_TOP_TBUS_DATA_11_8_MASK, + SERDES_25G_TOP_TBUS_DATA_11_8_SHIFT, + &val_high); + + *data = (val_high << 8) | val_low; + + return 0; +} + +#define AL_SERDES_25G_RX_ADV_PARAMS_ATT_MASK 0x07 +#define AL_SERDES_25G_RX_ADV_PARAMS_APG_MASK 0x03 +#define AL_SERDES_25G_RX_ADV_PARAMS_LFG_MASK 0x1F +#define AL_SERDES_25G_RX_ADV_PARAMS_HFG_MASK 0x1F +#define AL_SERDES_25G_RX_ADV_PARAMS_MBG_MASK 0x0F +#define AL_SERDES_25G_RX_ADV_PARAMS_MBF_MASK 0x0F +#define AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_CNT 8 +#define AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_MASK 0x1F +#define AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_SIGN_SHIFT 7 + +static void al_serdes_25g_rx_advanced_params_get( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + void *rx_params) +{ + struct al_serdes_25g_adv_rx_params *params = rx_params; + uint8_t value, err; + int8_t tap_weight; + uint8_t tap_sign; + int8_t *tap_ptr_arr[AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_CNT]; + int rc; + int i; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_PLE_ATT, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read att, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->att = value & AL_SERDES_25G_RX_ADV_PARAMS_ATT_MASK; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_GN_APG, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read apg, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->apg = value & AL_SERDES_25G_RX_ADV_PARAMS_APG_MASK; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_LFG, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read lfg, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->lfg = value & AL_SERDES_25G_RX_ADV_PARAMS_LFG_MASK; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_HFG_SQL, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read hfg, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->hfg = value & AL_SERDES_25G_RX_ADV_PARAMS_HFG_MASK; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_MBG, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read mbg, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->mbg = value & AL_SERDES_25G_RX_ADV_PARAMS_MBG_MASK; + + rc = al_serdes_25g_rx_leq_fsm_op(obj, lane, AL_SERDES_25G_RX_LEQ_FSM_OPCODE_READ, + AL_SERDES_25G_RX_LEQ_FSM_TARGET_EQ_MBF, 0, &value, &err); + if (rc || err) { + al_err("%s: al_serdes_25g_rx_leq_fsm_op failed to read mbf, rc %d, err %d\n", + __func__, rc, err); + return; + } + params->mbf = value & AL_SERDES_25G_RX_ADV_PARAMS_MBF_MASK; + + tap_ptr_arr[0] = ¶ms->dfe_first_tap_even0_ctrl; + tap_ptr_arr[1] = ¶ms->dfe_first_tap_even1_ctrl; + tap_ptr_arr[2] = ¶ms->dfe_first_tap_odd0_ctrl; + tap_ptr_arr[3] = ¶ms->dfe_first_tap_odd1_ctrl; + tap_ptr_arr[4] = ¶ms->dfe_second_tap_ctrl; + tap_ptr_arr[5] = ¶ms->dfe_third_tap_ctrl; + tap_ptr_arr[6] = ¶ms->dfe_fourth_tap_ctrl; + tap_ptr_arr[7] = ¶ms->dfe_fifth_tap_ctrl; + + for (i = 0; i < AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_CNT; i++) { + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_DFE_REFCLK_TAP_VAL_STATUS0_ADDR + i, + &value); + + tap_weight = value & AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_MASK; + tap_sign = (value & AL_BIT(AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_SIGN_SHIFT)) >> + AL_SERDES_25G_RX_ADV_PARAMS_DFE_TAP_SIGN_SHIFT; + if (tap_sign == 0) + tap_weight = 0 - tap_weight; + + *tap_ptr_arr[i] = tap_weight; + } +} + +#define AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_ADDR 0x0B +#define AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_MASK 0x3F +#define AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_SIGN_SHIFT 7 +#define AL_SERDES_25G_TX_DIAG_GCFSM2_CLK_DELAY_ADDR 0x0C +#define AL_SERDES_25G_TX_DIAG_GCFSM2_CLK_DELAY_MASK 0xFFF + +static void al_serdes_25g_tx_diag_info_get( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + void *tx_info) +{ + struct al_serdes_25g_tx_diag_info *info = tx_info; + uint8_t cal_x1, cal_x1_fixed, cal_x2, cal_xp5_fixed; + uint16_t val16, sign; + uint8_t val8, abs; + int rc; + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_TOP_AFE_TXCP_CTRL0_ADDR, + &val8); + info->regulated_supply = val8 & SERDES_25G_LANE_TOP_AFE_TXCP_CTRL0_REG_TXCP_TRIM_MASK; + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read dcd_trim, rc %d\n", + __func__, rc); + return; + } + + abs = val16 & AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_MASK; + sign = (val16 & AL_BIT(AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_SIGN_SHIFT)) >> + AL_SERDES_25G_TX_DIAG_GCFSM2_DCD_TRIM_SIGN_SHIFT; + if (sign) + info->dcd_trim = abs; + else + info->dcd_trim = 0 - abs; + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_TX_DIAG_GCFSM2_CLK_DELAY_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read clk_delay, rc %d\n", + __func__, rc); + return; + } + info->clk_delay = val16 & AL_SERDES_25G_TX_DIAG_GCFSM2_CLK_DELAY_MASK; + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_CM_TOP_AFE_TXTC_CTRL2_ADDR, + &val8); + cal_x1 = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL2_TXTC_CALP_X1_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL2_TXTC_CALP_X1_SHIFT; + cal_x1_fixed = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL2_TXTC_CALP_X1_FIXED_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL2_TXTC_CALP_X1_FIXED_SHIFT; + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_CM_TOP_AFE_TXTC_CTRL3_ADDR, + &val8); + cal_x2 = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL3_TXTC_CALP_X2_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL3_TXTC_CALP_X2_SHIFT; + cal_xp5_fixed = (val8 & + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL3_TXTC_CALP_XP5_FIXED_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL3_TXTC_CALP_XP5_FIXED_SHIFT; + info->calp_multiplied_by_2 = 4 * cal_x2 + 2 * cal_x1 + 2 * cal_x1_fixed + cal_xp5_fixed; + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_CM_TOP_AFE_TXTC_CTRL0_ADDR, + &val8); + cal_x1 = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL0_TXTC_CALN_X1_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL0_TXTC_CALN_X1_SHIFT; + cal_x1_fixed = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL0_TXTC_CALN_X1_FIXED_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL0_TXTC_CALN_X1_FIXED_SHIFT; + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_CM_TOP_AFE_TXTC_CTRL1_ADDR, + &val8); + cal_x2 = (val8 & SERDES_25G_CMU_TOP_AFE_TXTC_CTRL1_TXTC_CALN_X2_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL1_TXTC_CALN_X2_SHIFT; + cal_xp5_fixed = (val8 & + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL1_TXTC_CALN_XP5_FIXED_MASK) >> + SERDES_25G_CMU_TOP_AFE_TXTC_CTRL1_TXTC_CALN_XP5_FIXED_SHIFT; + info->caln_multiplied_by_2 = 4 * cal_x2 + 2 * cal_x1 + 2 * cal_x1_fixed + cal_xp5_fixed; +} + +#define AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_ABS_MASK 0x1F +#define AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_MASK 0x3F +#define AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_SIGN_SHIFT 5 +#define AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_MASK 0xFC0 +#define AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_SHIFT 6 +#define AL_SERDES_25G_RX_DIAG_LEQ_EQ_COUNT 5 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_LEQ_EQ_ADDR 0 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_LEQ_GAINSTAGE_ADDR 0x5 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_SUMMER_EVEN_ADDR 0x6 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_SUMMER_ODD_ADDR 0x7 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_VSCAN_EVEN_ADDR 0x8 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_VSCAN_ODD_ADDR 0x9 +#define AL_SERDES_25G_RX_DIAG_GCFSM2_CDR_VCO_FR_ADDR 0xF +#define AL_SERDES_25G_RX_DIAG_GCFSM2_CDR_VCO_FR_MASK 0xFFF +#define AL_SERDES_25G_RX_DIAG_TBUS_DATA_SLICER_EVEN_ADDR 0x11 +#define AL_SERDES_25G_RX_DIAG_TBUS_DATA_SLICER_ODD_ADDR 0x12 +#define AL_SERDES_25G_RX_DIAG_TBUS_EDGE_SLICER_ADDR 0x13 +#define AL_SERDES_25G_RX_DIAG_TBUS_EYE_SLICER_ADDR 0x23 +#define AL_SERDES_25G_RX_DIAG_TBUS_CDR_CLK_Q_ADDR 0x2 +#define AL_SERDES_25G_RX_DIAG_TBUS_CDR_CLK_I_ADDR 0x1 +#define AL_SERDES_25G_RX_DIAG_CDR_RXCLK_DLPF_L_ADDR 0x26 +#define AL_SERDES_25G_RX_DIAG_CDR_RXCLK_DLPF_H_ADDR 0x27 + +static inline void al_serdes_25g_rx_diag_5bit_signed_set(uint8_t packed_val, int8_t *ptr) +{ + uint8_t abs, sign; + + abs = packed_val & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_ABS_MASK; + sign = (packed_val & AL_BIT(AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_SIGN_SHIFT)) >> + AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_SIGN_SHIFT; + if (sign) + *ptr = abs; + else + *ptr = 0 - abs; +} + +static void al_serdes_25g_rx_diag_info_get( + struct al_serdes_grp_obj *obj, + enum al_serdes_lane lane, + void *rx_info) +{ + struct al_serdes_25g_rx_diag_info *info = rx_info; + uint16_t val16; + uint8_t val8, val8_2; + int rc; + int i; + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LOS_REFCLK_CALIBRATION_STATUS0_ADDR, + &val8); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->los_offset); + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_LOS_REFCLK_CALIBRATION_STATUS1_ADDR, + &val8); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->agc_offset); + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_LEQ_GAINSTAGE_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read leq_gainstage, rc %d\n", + __func__, rc); + return; + } + val8 = (uint8_t)val16; + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_gainstage_offset); + + for (i = 0; i < AL_SERDES_25G_RX_DIAG_LEQ_EQ_COUNT; i++) { + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_LEQ_EQ_ADDR + i, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read leq_eq %d, rc %d\n", + __func__, i, rc); + return; + } + val8 = (uint8_t)val16; + + switch (i) { + case 0: + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_eq1_offset); + break; + case 1: + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_eq2_offset); + break; + case 2: + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_eq3_offset); + break; + case 3: + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_eq4_offset); + break; + case 4: + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->leq_eq5_offset); + break; + default: + break; + } + } + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_SUMMER_EVEN_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read summer_even_offset, rc %d\n", + __func__, rc); + return; + } + val8 = (uint8_t)val16; + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->summer_even_offset); + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_SUMMER_ODD_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read summer_odd_offset, rc %d\n", + __func__, rc); + return; + } + val8 = (uint8_t)val16; + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->summer_odd_offset); + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_VSCAN_EVEN_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read vscan_even_offset, rc %d\n", + __func__, rc); + return; + } + val8 = (uint8_t)val16; + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->vscan_even_offset); + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_VSCAN_ODD_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read vscan_odd_offset, rc %d\n", + __func__, rc); + return; + } + val8 = (uint8_t)val16; + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->vscan_odd_offset); + + al_serdes_25g_tbus_read( + obj, + lane, + AL_SERDES_25G_TBUS_OBJ_LANE, + AL_SERDES_25G_RX_DIAG_TBUS_DATA_SLICER_EVEN_ADDR, + &val16); + val8 = (uint8_t)(val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_MASK); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->data_slicer_even0_offset); + val8 = (uint8_t)((val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_MASK) >> + AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_SHIFT); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->data_slicer_even1_offset); + + al_serdes_25g_tbus_read( + obj, + lane, + AL_SERDES_25G_TBUS_OBJ_LANE, + AL_SERDES_25G_RX_DIAG_TBUS_DATA_SLICER_ODD_ADDR, + &val16); + val8 = (uint8_t)(val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_MASK); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->data_slicer_odd0_offset); + val8 = (uint8_t)((val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_MASK) >> + AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_SHIFT); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->data_slicer_odd1_offset); + + al_serdes_25g_tbus_read( + obj, + lane, + AL_SERDES_25G_TBUS_OBJ_LANE, + AL_SERDES_25G_RX_DIAG_TBUS_EDGE_SLICER_ADDR, + &val16); + val8 = (uint8_t)(val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_MASK); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->edge_slicer_even_offset); + val8 = (uint8_t)((val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_MASK) >> + AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_SHIFT); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->edge_slicer_odd_offset); + + al_serdes_25g_tbus_read( + obj, + lane, + AL_SERDES_25G_TBUS_OBJ_LANE, + AL_SERDES_25G_RX_DIAG_TBUS_EYE_SLICER_ADDR, + &val16); + val8 = (uint8_t)(val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_MASK); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->eye_slicer_even_offset); + val8 = (uint8_t)((val16 & AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_MASK) >> + AL_SERDES_25G_RX_DIAG_SIGNED_5BIT_HIGH_SHIFT); + al_serdes_25g_rx_diag_5bit_signed_set(val8, &info->eye_slicer_odd_offset); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL0_ADDR, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL0_RXCDR_HSCAN_CLKQ_MASK, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL0_RXCDR_HSCAN_CLKQ_SHIFT, + &info->cdr_clk_q); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL1_ADDR, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL1_RXCDR_HSCAN_CLKI_MASK, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL1_RXCDR_HSCAN_CLKI_SHIFT, + &info->cdr_clk_i); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL2_ADDR, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL2_RXCDR_HSCAN_EYE_MASK, + SERDES_25G_LANE_CDR_REFCLK_AFE_PI_CTRL2_RXCDR_HSCAN_EYE_SHIFT, + &info->cdr_dll); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_CDR_REFCLK_AFE_VCO_CTRL2_ADDR, + SERDES_25G_LANE_CDR_REFCLK_AFE_VCO_CTRL2_RXCDR_DOSC_MASK, + SERDES_25G_LANE_CDR_REFCLK_AFE_VCO_CTRL2_RXCDR_DOSC_SHIFT, + &info->cdr_vco_dosc); + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_CDR_RXCLK_LOAD_MODE_CTRL1_ADDR, + &val8_2); + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_CDR_RXCLK_LOAD_MODE_CTRL0_ADDR, + &val8); + val8_2 &= SERDES_25G_LANE_CDR_RXCLK_LOAD_MODE_CTRL1_DLPF_VAL_8_MASK; + info->cdr_dlpf = (uint16_t)val8_2 << 8 | val8; + + rc = al_serdes_25g_gcfsm2_read( + obj, + lane, + AL_SERDES_25G_RX_DIAG_GCFSM2_CDR_VCO_FR_ADDR, + &val16); + if (rc) { + al_err("%s: al_serdes_25g_gcfsm2_read failed to read cdr_vco_fr, rc %d\n", + __func__, rc); + return; + } + info->cdr_vco_fr = val16 & AL_SERDES_25G_RX_DIAG_GCFSM2_CDR_VCO_FR_MASK; + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_AFE_PLE_CTRL0_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_AFE_PLE_CTRL0_RXLEQ_PLE_BLW_ZERO_MASK, + SERDES_25G_LANE_LEQ_REFCLK_AFE_PLE_CTRL0_RXLEQ_PLE_BLW_ZERO_SHIFT, + &info->ple_resistance); + + al_serdes_25g_reg_read( + obj, + (enum al_serdes_reg_page)lane, + 0, + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL0_ADDR, + &val8); + + info->rx_term_mode = (val8 & SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL0_RXTERM_HIZ_MASK) >> + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL0_RXTERM_HIZ_SHIFT; + + info->rx_coupling = (val8 & SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL0_RXTERM_VCM_GND_MASK) >> + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL0_RXTERM_VCM_GND_SHIFT; + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL1_ADDR, + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL1_RXTERM_VAL_MASK, + SERDES_25G_LANE_TOP_AFE_RXTERM_CTRL1_RXTERM_VAL_SHIFT, + &info->rx_term_cal_code); + + al_serdes_25g_reg_masked_read( + obj, + (enum al_serdes_reg_page)lane, + SERDES_25G_LANE_LEQ_REFCLK_AFE_BIAS_CTRL1_ADDR, + SERDES_25G_LANE_LEQ_REFCLK_AFE_BIAS_CTRL1_RXLEQ_BIASI_TRIM_MASK, + SERDES_25G_LANE_LEQ_REFCLK_AFE_BIAS_CTRL1_RXLEQ_BIASI_TRIM_SHIFT, + &info->rx_sheet_res_cal_code); +} + +/******************************************************************************/ +/******************************************************************************/ +int al_serdes_25g_handle_init( + void __iomem *serdes_regs_base, + struct al_serdes_grp_obj *obj) +{ + al_dbg( + "%s(%p, %p)\n", + __func__, + serdes_regs_base, + obj); + + al_memset(obj, 0, sizeof(struct al_serdes_grp_obj)); + + obj->regs_base = (struct al_serdes_regs *)serdes_regs_base; + obj->type_get = al_serdes_25g_type_get; + obj->reg_read = al_serdes_25g_reg_read; + obj->reg_write = al_serdes_25g_reg_write; + obj->bist_overrides_enable = NULL; + obj->bist_overrides_disable = NULL; + obj->rx_rate_change = NULL; + obj->group_pm_set = NULL; + obj->lane_pm_set = NULL; + obj->pma_hard_reset_group = NULL; + obj->pma_hard_reset_lane = NULL; + obj->loopback_control = NULL; + obj->bist_pattern_select = AL_SRDS_ADV_SRVC(al_serdes_25g_bist_pattern_select); + obj->bist_tx_enable = AL_SRDS_ADV_SRVC(al_serdes_25g_bist_tx_enable); + obj->bist_tx_err_inject = NULL; + obj->bist_rx_enable = AL_SRDS_ADV_SRVC(al_serdes_25g_bist_rx_enable); + obj->bist_rx_status = AL_SRDS_ADV_SRVC(al_serdes_25g_bist_rx_status); + obj->tx_deemph_preset = NULL; + obj->tx_deemph_inc = NULL; + obj->tx_deemph_dec = NULL; + obj->eye_measure_run = NULL; + obj->eye_diag_sample = NULL; + obj->eye_diag_run = AL_SRDS_ADV_SRVC(al_serdes_25g_eye_diag_run); + obj->cdr_is_locked = AL_SRDS_ADV_SRVC(al_serdes_25g_cdr_is_locked); + obj->rx_valid = AL_SRDS_ADV_SRVC(al_serdes_25g_rx_valid); + obj->signal_is_detected = AL_SRDS_ADV_SRVC(al_serdes_25g_signal_is_detected); + obj->tx_advanced_params_set = AL_SRDS_ADV_SRVC(al_serdes_25g_tx_advanced_params_set); + obj->tx_advanced_params_get = AL_SRDS_ADV_SRVC(al_serdes_25g_tx_advanced_params_get); + obj->rx_advanced_params_set = NULL; + obj->rx_advanced_params_get = AL_SRDS_ADV_SRVC(al_serdes_25g_rx_advanced_params_get); + obj->tx_diag_info_get = AL_SRDS_ADV_SRVC(al_serdes_25g_tx_diag_info_get); + obj->rx_diag_info_get = AL_SRDS_ADV_SRVC(al_serdes_25g_rx_diag_info_get); + obj->mode_set_sgmii = NULL; + obj->mode_set_kr = NULL; + obj->rx_equalization = AL_SRDS_ADV_SRVC(al_serdes_25g_rx_equalization); + obj->calc_eye_size = AL_SRDS_ADV_SRVC(al_serdes_25g_calc_eye_size); + obj->sris_config = NULL; + + return 0; +} + |