/******************************************************************************* 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; }