LCOV - code coverage report
Current view: top level - drivers/net/ntnic/nim - i2c_nim.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 336 0.0 %
Date: 2025-04-03 19:37:06 Functions: 0 29 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 157 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * SPDX-License-Identifier: BSD-3-Clause
       3                 :            :  * Copyright(c) 2023 Napatech A/S
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <string.h>
       7                 :            : 
       8                 :            : #include "nthw_drv.h"
       9                 :            : #include "i2c_nim.h"
      10                 :            : #include "ntlog.h"
      11                 :            : #include "nt_util.h"
      12                 :            : #include "ntnic_mod_reg.h"
      13                 :            : #include "qsfp_registers.h"
      14                 :            : #include "nim_defines.h"
      15                 :            : 
      16                 :            : int nim_agx_read_id(struct nim_i2c_ctx *ctx);
      17                 :            : static void nim_agx_read(struct nim_i2c_ctx *ctx, uint8_t dev_addr, uint8_t reg_addr,
      18                 :            :         uint8_t data_len, void *p_data);
      19                 :            : static void nim_agx_write(struct nim_i2c_ctx *ctx, uint8_t dev_addr, uint8_t reg_addr,
      20                 :            :         uint8_t data_len, void *p_data);
      21                 :            : 
      22                 :            : #define NIM_READ false
      23                 :            : #define NIM_WRITE true
      24                 :            : #define NIM_PAGE_SEL_REGISTER 127
      25                 :            : #define NIM_I2C_0XA0 0xA0       /* Basic I2C address */
      26                 :            : 
      27                 :            : 
      28                 :          0 : static bool page_addressing(nt_nim_identifier_t id)
      29                 :            : {
      30         [ #  # ]:          0 :         switch (id) {
      31                 :            :         case NT_NIM_QSFP:
      32                 :            :         case NT_NIM_QSFP_PLUS:
      33                 :            :         case NT_NIM_QSFP28:
      34                 :            :                 return true;
      35                 :            : 
      36                 :          0 :         default:
      37                 :          0 :                 NT_LOG(DBG, NTNIC, "Unknown NIM identifier %d", id);
      38                 :          0 :                 return false;
      39                 :            :         }
      40                 :            : }
      41                 :            : 
      42                 :            : static nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx)
      43                 :            : {
      44                 :          0 :         return (nt_nim_identifier_t)ctx->nim_id;
      45                 :            : }
      46                 :            : 
      47                 :          0 : static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write, uint16_t lin_addr,
      48                 :            :         uint8_t i2c_addr, uint8_t a_reg_addr, uint8_t seq_cnt,
      49                 :            :         uint8_t *p_data)
      50                 :            : {
      51                 :            :         /* Divide i2c_addr by 2 because nthw_iic_read/writeData multiplies by 2 */
      52                 :          0 :         const uint8_t i2c_devaddr = i2c_addr / 2U;
      53                 :            :         (void)lin_addr; /* Unused */
      54                 :            : 
      55         [ #  # ]:          0 :         if (do_write) {
      56         [ #  # ]:          0 :                 if (ctx->type == I2C_HWIIC) {
      57                 :          0 :                         return nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt,
      58                 :            :                                         p_data);
      59                 :            :                 }
      60                 :            : 
      61                 :          0 :                 nim_agx_write(ctx, i2c_addr, a_reg_addr, seq_cnt, p_data);
      62                 :          0 :                 return 0;
      63                 :            :         }
      64                 :            : 
      65         [ #  # ]:          0 :         if (ctx->type == I2C_HWIIC)
      66                 :          0 :                 return nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, a_reg_addr, seq_cnt, p_data);
      67                 :            : 
      68                 :          0 :         nim_agx_read(ctx, i2c_addr, a_reg_addr, seq_cnt, p_data);
      69                 :          0 :         return 0;
      70                 :            : }
      71                 :            : 
      72                 :            : /*
      73                 :            :  * ------------------------------------------------------------------------------
      74                 :            :  * Selects a new page for page addressing. This is only relevant if the NIM
      75                 :            :  * supports this. Since page switching can take substantial time the current page
      76                 :            :  * select is read and subsequently only changed if necessary.
      77                 :            :  * Important:
      78                 :            :  * XFP Standard 8077, Ver 4.5, Page 61 states that:
      79                 :            :  * If the host attempts to write a table select value which is not supported in
      80                 :            :  * a particular module, the table select byte will revert to 01h.
      81                 :            :  * This can lead to some surprising result that some pages seems to be duplicated.
      82                 :            :  * ------------------------------------------------------------------------------
      83                 :            :  */
      84                 :            : 
      85                 :          0 : static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel)
      86                 :            : {
      87                 :            :         uint8_t curr_page_sel;
      88                 :            : 
      89                 :            :         /* Read the current page select value */
      90         [ #  # ]:          0 :         if (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
      91                 :            :                         NIM_PAGE_SEL_REGISTER, sizeof(curr_page_sel),
      92                 :            :                         &curr_page_sel) != 0) {
      93                 :            :                 return -1;
      94                 :            :         }
      95                 :            : 
      96                 :            :         /* Only write new page select value if necessary */
      97         [ #  # ]:          0 :         if (page_sel != curr_page_sel) {
      98         [ #  # ]:          0 :                 if (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER, NIM_I2C_0XA0,
      99                 :            :                                 NIM_PAGE_SEL_REGISTER, sizeof(page_sel),
     100                 :            :                                 &page_sel) != 0) {
     101                 :          0 :                         return -1;
     102                 :            :                 }
     103                 :            :         }
     104                 :            : 
     105                 :            :         return 0;
     106                 :            : }
     107                 :            : 
     108                 :          0 : static int nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing, uint16_t lin_addr,
     109                 :            :         uint16_t length, uint8_t *p_data, bool do_write)
     110                 :            : {
     111                 :            :         uint16_t i;
     112                 :            :         uint8_t a_reg_addr;     /* The actual register address in I2C device */
     113                 :            :         uint8_t i2c_addr;
     114                 :            :         int block_size = 128;   /* Equal to size of MSA pages */
     115                 :            :         int seq_cnt;
     116                 :            :         int max_seq_cnt = 1;
     117                 :            :         int multi_byte = 1;     /* One byte per I2C register is default */
     118                 :            : 
     119         [ #  # ]:          0 :         for (i = 0; i < length;) {
     120                 :            :                 bool use_page_select = false;
     121                 :            : 
     122                 :            :                 /*
     123                 :            :                  * Find out how much can be read from the current block in case of
     124                 :            :                  * single byte access
     125                 :            :                  */
     126                 :            :                 if (multi_byte == 1)
     127                 :          0 :                         max_seq_cnt = block_size - (lin_addr % block_size);
     128                 :            : 
     129         [ #  # ]:          0 :                 if (m_page_addressing) {
     130         [ #  # ]:          0 :                         if (lin_addr >= 128) {       /* Only page setup above this address */
     131                 :            :                                 use_page_select = true;
     132                 :            : 
     133                 :            :                                 /* Map to [128..255] of 0xA0 device */
     134                 :          0 :                                 a_reg_addr = (uint8_t)(block_size + (lin_addr % block_size));
     135                 :            : 
     136                 :            :                         } else {
     137                 :          0 :                                 a_reg_addr = (uint8_t)lin_addr;
     138                 :            :                         }
     139                 :            : 
     140                 :            :                         i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
     141                 :            : 
     142         [ #  # ]:          0 :                 } else if (lin_addr >= 256) {
     143                 :            :                         /* Map to address [0..255] of 0xA2 device */
     144                 :          0 :                         a_reg_addr = (uint8_t)(lin_addr - 256);
     145                 :            :                         i2c_addr = NIM_I2C_0XA2;
     146                 :            : 
     147                 :            :                 } else {
     148                 :          0 :                         a_reg_addr = (uint8_t)lin_addr;
     149                 :            :                         i2c_addr = NIM_I2C_0XA0;/* Base I2C address */
     150                 :            :                 }
     151                 :            : 
     152                 :            :                 /* Now actually do the reading/writing */
     153                 :          0 :                 seq_cnt = length - i;   /* Number of remaining bytes */
     154                 :            : 
     155                 :            :                 if (seq_cnt > max_seq_cnt)
     156                 :            :                         seq_cnt = max_seq_cnt;
     157                 :            : 
     158                 :            :                 /*
     159                 :            :                  * Read a number of bytes without explicitly specifying a new address.
     160                 :            :                  * This can speed up I2C access since automatic incrementation of the
     161                 :            :                  * I2C device internal address counter can be used. It also allows
     162                 :            :                  * a HW implementation, that can deal with block access.
     163                 :            :                  * Furthermore it also allows for access to data that must be accessed
     164                 :            :                  * as 16bit words reading two bytes at each address eg PHYs.
     165                 :            :                  */
     166         [ #  # ]:          0 :                 if (use_page_select) {
     167         [ #  # ]:          0 :                         if (nim_setup_page(ctx, (uint8_t)((lin_addr / 128) - 1)) != 0) {
     168                 :          0 :                                 NT_LOG(ERR, NTNIC,
     169                 :            :                                         "Cannot set up page for linear address %u", lin_addr);
     170                 :          0 :                                 return -1;
     171                 :            :                         }
     172                 :            :                 }
     173                 :            : 
     174         [ #  # ]:          0 :                 if (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr, a_reg_addr,
     175                 :            :                                 (uint8_t)seq_cnt, p_data) != 0) {
     176                 :          0 :                         NT_LOG(ERR, NTNIC, " Call to nim_read_write_i2c_data failed");
     177                 :          0 :                         return -1;
     178                 :            :                 }
     179                 :            : 
     180                 :          0 :                 p_data += seq_cnt;
     181                 :          0 :                 i = (uint16_t)(i + seq_cnt);
     182                 :          0 :                 lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));
     183                 :            :         }
     184                 :            : 
     185                 :            :         return 0;
     186                 :            : }
     187                 :            : 
     188                 :          0 : static int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data)
     189                 :            : {
     190                 :            :         /* Wrapper for using Mutex for QSFP TODO */
     191                 :          0 :         return nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), lin_addr, length, data,
     192                 :            :                         NIM_READ);
     193                 :            : }
     194                 :            : 
     195                 :          0 : static int write_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, void *data)
     196                 :            : {
     197                 :            :         /* Wrapper for using Mutex for QSFP TODO */
     198                 :          0 :         return nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), lin_addr, length, data,
     199                 :            :                 NIM_WRITE);
     200                 :            : }
     201                 :            : 
     202                 :            : /* Read and return a single byte */
     203                 :            : static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr)
     204                 :            : {
     205                 :            :         uint8_t data;
     206                 :          0 :         read_data_lin(ctx, addr, sizeof(data), &data);
     207                 :          0 :         return data;
     208                 :            : }
     209                 :            : 
     210                 :            : static int nim_read_id(nim_i2c_ctx_t *ctx)
     211                 :            : {
     212                 :            :         /* We are only reading the first byte so we don't care about pages here. */
     213                 :            :         const bool USE_PAGE_ADDRESSING = false;
     214                 :            : 
     215         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, USE_PAGE_ADDRESSING, NIM_IDENTIFIER_ADDR,
     216                 :            :                         sizeof(ctx->nim_id), &ctx->nim_id, NIM_READ) != 0) {
     217                 :            :                 return -1;
     218                 :            :         }
     219                 :            : 
     220                 :            :         return 0;
     221                 :            : }
     222                 :            : 
     223                 :          0 : static int i2c_nim_common_construct(nim_i2c_ctx_p ctx)
     224                 :            : {
     225                 :          0 :         ctx->nim_id = 0;
     226                 :            :         int res;
     227                 :            : 
     228         [ #  # ]:          0 :         if (ctx->type == I2C_HWIIC)
     229                 :            :                 res = nim_read_id(ctx);
     230                 :            : 
     231                 :            :         else
     232                 :          0 :                 res = nim_agx_read_id(ctx);
     233                 :            : 
     234         [ #  # ]:          0 :         if (res) {
     235                 :          0 :                 NT_LOG(ERR, NTNIC, "Can't read NIM id.");
     236                 :          0 :                 return res;
     237                 :            :         }
     238                 :            : 
     239                 :          0 :         memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name));
     240                 :          0 :         memset(ctx->prod_no, 0, sizeof(ctx->prod_no));
     241                 :          0 :         memset(ctx->serial_no, 0, sizeof(ctx->serial_no));
     242                 :          0 :         memset(ctx->date, 0, sizeof(ctx->date));
     243                 :          0 :         memset(ctx->rev, 0, sizeof(ctx->rev));
     244                 :            : 
     245                 :          0 :         ctx->content_valid = false;
     246                 :          0 :         memset(ctx->len_info, 0, sizeof(ctx->len_info));
     247                 :          0 :         ctx->pwr_level_req = 0;
     248                 :          0 :         ctx->pwr_level_cur = 0;
     249                 :          0 :         ctx->avg_pwr = false;
     250                 :          0 :         ctx->tx_disable = false;
     251                 :          0 :         ctx->lane_idx = -1;
     252                 :          0 :         ctx->lane_count = 1;
     253                 :          0 :         ctx->options = 0;
     254                 :          0 :         return 0;
     255                 :            : }
     256                 :            : 
     257                 :            : /*
     258                 :            :  * Read vendor information at a certain address. Any trailing whitespace is
     259                 :            :  * removed and a missing string termination in the NIM data is handled.
     260                 :            :  */
     261                 :          0 : static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, uint8_t max_len, char *p_data)
     262                 :            : {
     263                 :          0 :         const bool pg_addr = page_addressing(ctx->nim_id);
     264                 :            :         int i;
     265                 :            :         /* Subtract "1" from max_len that includes a terminating "0" */
     266                 :            : 
     267         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1), (uint8_t *)p_data,
     268                 :            :                         NIM_READ) != 0) {
     269                 :            :                 return -1;
     270                 :            :         }
     271                 :            : 
     272                 :            :         /* Terminate at first found white space */
     273         [ #  # ]:          0 :         for (i = 0; i < max_len - 1; i++) {
     274         [ #  # ]:          0 :                 if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || *p_data == '\v' ||
     275                 :            :                         *p_data == '\f' || *p_data == '\r') {
     276                 :          0 :                         *p_data = '\0';
     277                 :          0 :                         return 0;
     278                 :            :                 }
     279                 :            : 
     280                 :          0 :                 p_data++;
     281                 :            :         }
     282                 :            : 
     283                 :            :         /*
     284                 :            :          * Add line termination as the very last character, if it was missing in the
     285                 :            :          * NIM data
     286                 :            :          */
     287                 :          0 :         *p_data = '\0';
     288                 :          0 :         return 0;
     289                 :            : }
     290                 :            : 
     291                 :          0 : static void qsfp_read_vendor_info(nim_i2c_ctx_t *ctx)
     292                 :            : {
     293                 :          0 :         nim_read_vendor_info(ctx, QSFP_VENDOR_NAME_LIN_ADDR, sizeof(ctx->vendor_name),
     294                 :          0 :                 ctx->vendor_name);
     295                 :          0 :         nim_read_vendor_info(ctx, QSFP_VENDOR_PN_LIN_ADDR, sizeof(ctx->prod_no), ctx->prod_no);
     296                 :          0 :         nim_read_vendor_info(ctx, QSFP_VENDOR_SN_LIN_ADDR, sizeof(ctx->serial_no), ctx->serial_no);
     297                 :          0 :         nim_read_vendor_info(ctx, QSFP_VENDOR_DATE_LIN_ADDR, sizeof(ctx->date), ctx->date);
     298                 :          0 :         nim_read_vendor_info(ctx, QSFP_VENDOR_REV_LIN_ADDR, (uint8_t)(sizeof(ctx->rev) - 2),
     299                 :          0 :                 ctx->rev);   /*OBS Only two bytes*/
     300                 :          0 : }
     301   [ #  #  #  # ]:          0 : static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
     302                 :            : {
     303                 :            :         int res = 0;    /* unused due to no readings from HW */
     304                 :            : 
     305                 :            :         RTE_ASSERT(ctx && state);
     306                 :            :         RTE_ASSERT(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized");
     307                 :            : 
     308                 :            :         (void)memset(state, 0, sizeof(*state));
     309                 :            : 
     310   [ #  #  #  # ]:          0 :         switch (ctx->nim_id) {
     311                 :          0 :         case 12U:
     312                 :          0 :                 state->br = 10U;/* QSFP: 4 x 1G = 4G */
     313                 :          0 :                 break;
     314                 :            : 
     315                 :          0 :         case 13U:
     316                 :          0 :                 state->br = 103U;    /* QSFP+: 4 x 10G = 40G */
     317                 :          0 :                 break;
     318                 :            : 
     319                 :          0 :         case 17U:
     320                 :          0 :                 state->br = 255U;    /* QSFP28: 4 x 25G = 100G */
     321                 :          0 :                 break;
     322                 :            : 
     323                 :          0 :         default:
     324                 :          0 :                 NT_LOG(INF, NTNIC, "nim_id = %u is not an QSFP/QSFP+/QSFP28 module", ctx->nim_id);
     325                 :            :                 res = -1;
     326                 :            :         }
     327                 :            : 
     328                 :          0 :         return res;
     329                 :            : }
     330                 :            : 
     331                 :          0 : int nthw_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)
     332                 :            : {
     333                 :          0 :         return qsfp_nim_state_build(ctx, state);
     334                 :            : }
     335                 :            : 
     336                 :          0 : const char *nthw_nim_id_to_text(uint8_t nim_id)
     337                 :            : {
     338   [ #  #  #  #  :          0 :         switch (nim_id) {
                      # ]
     339                 :            :         case 0x0:
     340                 :            :                 return "UNKNOWN";
     341                 :            : 
     342                 :          0 :         case 0x0C:
     343                 :          0 :                 return "QSFP";
     344                 :            : 
     345                 :          0 :         case 0x0D:
     346                 :          0 :                 return "QSFP+";
     347                 :            : 
     348                 :          0 :         case 0x11:
     349                 :          0 :                 return "QSFP28";
     350                 :            : 
     351                 :          0 :         default:
     352                 :          0 :                 return "ILLEGAL!";
     353                 :            :         }
     354                 :            : }
     355                 :            : 
     356                 :            : /*
     357                 :            :  * Disable laser for specific lane or all lanes
     358                 :            :  */
     359                 :          0 : int nthw_nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable, int lane_idx)
     360                 :            : {
     361                 :            :         uint8_t value;
     362                 :            :         uint8_t mask;
     363                 :          0 :         const bool pg_addr = page_addressing(ctx->nim_id);
     364                 :            : 
     365         [ #  # ]:          0 :         if (lane_idx < 0)    /* If no lane is specified then all lanes */
     366                 :            :                 mask = QSFP_SOFT_TX_ALL_DISABLE_BITS;
     367                 :            : 
     368                 :            :         else
     369                 :          0 :                 mask = (uint8_t)(1U << lane_idx);
     370                 :            : 
     371         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
     372                 :            :                         &value, NIM_READ) != 0) {
     373                 :            :                 return -1;
     374                 :            :         }
     375                 :            : 
     376         [ #  # ]:          0 :         if (disable)
     377                 :          0 :                 value |= mask;
     378                 :            : 
     379                 :            :         else
     380                 :          0 :                 value &= (uint8_t)(~mask);
     381                 :            : 
     382         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, sizeof(value),
     383                 :            :                         &value, NIM_WRITE) != 0) {
     384                 :          0 :                 return -1;
     385                 :            :         }
     386                 :            : 
     387                 :            :         return 0;
     388                 :            : }
     389                 :            : 
     390                 :            : /*
     391                 :            :  * Import length info in various units from NIM module data and convert to meters
     392                 :            :  */
     393                 :            : static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info, uint16_t *p_nim_units)
     394                 :            : {
     395                 :            :         size_t i;
     396                 :            : 
     397         [ #  # ]:          0 :         for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++)
     398         [ #  # ]:          0 :                 if (*(p_nim_len_info + i) == 255) {
     399                 :          0 :                         ctx->len_info[i] = 65535;
     400                 :            : 
     401                 :            :                 } else {
     402                 :          0 :                         uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i);
     403                 :            : 
     404         [ #  # ]:          0 :                         if (len > 65535)
     405                 :          0 :                                 ctx->len_info[i] = 65535;
     406                 :            : 
     407                 :            :                         else
     408                 :          0 :                                 ctx->len_info[i] = (uint16_t)len;
     409                 :            :                 }
     410                 :            : }
     411                 :            : 
     412                 :          0 : static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx)
     413                 :            : {
     414                 :          0 :         const bool pg_addr = page_addressing(ctx->nim_id);
     415                 :            :         uint8_t options;
     416                 :            :         uint8_t value;
     417                 :            :         uint8_t nim_len_info[5];
     418                 :          0 :         uint16_t nim_units[5] = { 1000, 2, 1, 1, 1 };   /* QSFP MSA units in meters */
     419                 :          0 :         const char *yes_no[2] = { "No", "Yes" };
     420                 :            :         (void)yes_no;
     421                 :          0 :         NT_LOG(DBG, NTNIC, "Instance %d: NIM id: %s (%d)", ctx->instance,
     422                 :            :                 nthw_nim_id_to_text(ctx->nim_id), ctx->nim_id);
     423                 :            : 
     424                 :            :         /* Read DMI options */
     425         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR, sizeof(options),
     426                 :            :                         &options, NIM_READ) != 0) {
     427                 :            :                 return -1;
     428                 :            :         }
     429                 :            : 
     430                 :          0 :         ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT;
     431                 :          0 :         NT_LOG(DBG, NTNIC, "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)", ctx->instance,
     432                 :            :                 yes_no[ctx->avg_pwr]);
     433                 :            : 
     434                 :          0 :         qsfp_read_vendor_info(ctx);
     435                 :          0 :         NT_LOG(DBG, NTNIC,
     436                 :            :                 "Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)",
     437                 :            :                 ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no, ctx->date, ctx->rev);
     438                 :            : 
     439         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info),
     440                 :            :                         nim_len_info, NIM_READ) != 0) {
     441                 :            :                 return -1;
     442                 :            :         }
     443                 :            : 
     444                 :            :         /*
     445                 :            :          * Returns supported length information in meters for various fibers as 5 indivi-
     446                 :            :          * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper]
     447                 :            :          * If no length information is available for a certain entry, the returned value
     448                 :            :          * will be zero. This will be the case for SFP modules - EBW entry.
     449                 :            :          * If the MSBit is set the returned value in the lower 31 bits indicates that the
     450                 :            :          * supported length is greater than this.
     451                 :            :          */
     452                 :            : 
     453                 :            :         nim_import_len_info(ctx, nim_len_info, nim_units);
     454                 :            : 
     455                 :            :         /* Read required power level */
     456         [ #  # ]:          0 :         if (nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER, sizeof(value), &value,
     457                 :            :                         NIM_READ) != 0) {
     458                 :            :                 return -1;
     459                 :            :         }
     460                 :            : 
     461                 :            :         /*
     462                 :            :          * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43:
     463                 :            :          * If power class >= 5 setHighPower must be called for the module to be fully
     464                 :            :          * functional
     465                 :            :          */
     466         [ #  # ]:          0 :         if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) {
     467                 :            :                 /* NIM in power class 1 - 4 */
     468                 :          0 :                 ctx->pwr_level_req = (uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) + 1);
     469                 :            : 
     470                 :            :         } else {
     471                 :            :                 /* NIM in power class 5 - 7 */
     472                 :          0 :                 ctx->pwr_level_req = (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4);
     473                 :            :         }
     474                 :            : 
     475                 :            :         return 0;
     476                 :            : }
     477                 :            : 
     478                 :          0 : static void qsfp28_find_port_params(nim_i2c_ctx_p ctx)
     479                 :            : {
     480                 :            :         uint8_t fiber_chan_speed;
     481                 :            : 
     482                 :            :         /* Table 6-17 SFF-8636 */
     483                 :          0 :         read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed);
     484                 :            : 
     485         [ #  # ]:          0 :         if (fiber_chan_speed & (1 << 7)) {
     486                 :            :                 /* SFF-8024, Rev 4.7, Table 4-4 */
     487                 :          0 :                 uint8_t extended_specification_compliance_code = 0;
     488                 :          0 :                 read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1,
     489                 :            :                         &extended_specification_compliance_code);
     490                 :            : 
     491   [ #  #  #  #  :          0 :                 switch (extended_specification_compliance_code) {
             #  #  #  #  
                      # ]
     492                 :          0 :                 case 0x02:
     493                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_SR4;
     494                 :          0 :                         break;
     495                 :            : 
     496                 :          0 :                 case 0x03:
     497                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_LR4;
     498                 :          0 :                         break;
     499                 :            : 
     500                 :          0 :                 case 0x0B:
     501                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L;
     502                 :          0 :                         break;
     503                 :            : 
     504                 :          0 :                 case 0x0C:
     505                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S;
     506                 :          0 :                         break;
     507                 :            : 
     508                 :          0 :                 case 0x0D:
     509                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N;
     510                 :          0 :                         break;
     511                 :            : 
     512                 :          0 :                 case 0x25:
     513                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_DR;
     514                 :          0 :                         break;
     515                 :            : 
     516                 :          0 :                 case 0x26:
     517                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_FR;
     518                 :          0 :                         break;
     519                 :            : 
     520                 :          0 :                 case 0x27:
     521                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28_LR;
     522                 :          0 :                         break;
     523                 :            : 
     524                 :          0 :                 default:
     525                 :          0 :                         ctx->port_type = NT_PORT_TYPE_QSFP28;
     526                 :            :                 }
     527                 :            : 
     528                 :            :         } else {
     529                 :          0 :                 ctx->port_type = NT_PORT_TYPE_QSFP28;
     530                 :            :         }
     531                 :          0 : }
     532                 :            : 
     533                 :            : /*
     534                 :            :  * If true the user must actively select the desired rate. If false the module
     535                 :            :  * however can still support several rates without the user is required to select
     536                 :            :  * one of them. Supported rates must then be deduced from the product number.
     537                 :            :  * SFF-8636, Rev 2.10a:
     538                 :            :  * p40: 6.2.7 Rate Select
     539                 :            :  * p85: A.2 Rate Select
     540                 :            :  */
     541                 :          0 : static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx)
     542                 :            : {
     543                 :            :         const uint8_t ext_rate_select_compl_reg_addr = 141;
     544                 :            :         const uint8_t options_reg_addr = 195;
     545                 :            :         const uint8_t enh_options_reg_addr = 221;
     546                 :            : 
     547                 :          0 :         uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & 0x01;     /* bit: 5 */
     548                 :            : 
     549         [ #  # ]:          0 :         if (rate_select_ena == 0)
     550                 :            :                 return false;
     551                 :            : 
     552                 :          0 :         uint8_t rate_select_type =
     553                 :          0 :                 (read_byte(ctx, enh_options_reg_addr) >> 2) & 0x03;   /* bit 3..2 */
     554                 :            : 
     555         [ #  # ]:          0 :         if (rate_select_type != 2) {
     556                 :          0 :                 NT_LOG(DBG, NTNIC, "NIM has unhandled rate select type (%d)", rate_select_type);
     557                 :          0 :                 return false;
     558                 :            :         }
     559                 :            : 
     560                 :          0 :         uint8_t ext_rate_select_ver =
     561                 :            :                 read_byte(ctx, ext_rate_select_compl_reg_addr) & 0x03;      /* bit 1..0 */
     562                 :            : 
     563         [ #  # ]:          0 :         if (ext_rate_select_ver != 0x02) {
     564                 :          0 :                 NT_LOG(DBG, NTNIC, "NIM has unhandled extended rate select version (%d)",
     565                 :            :                         ext_rate_select_ver);
     566                 :          0 :                 return false;
     567                 :            :         }
     568                 :            : 
     569                 :            :         return true;    /* When true selectRate() can be used */
     570                 :            : }
     571                 :            : 
     572                 :          0 : static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx)
     573                 :            : {
     574         [ #  # ]:          0 :         if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || ctx->port_type == NT_PORT_TYPE_QSFP28_DR ||
     575                 :            :                 ctx->port_type == NT_PORT_TYPE_QSFP28_LR) {
     576         [ #  # ]:          0 :                 if (ctx->lane_idx < 0)
     577                 :          0 :                         ctx->speed_mask = NT_LINK_SPEED_100G;
     578                 :            : 
     579                 :            :                 else
     580                 :            :                         /* PAM-4 modules can only run on all lanes together */
     581                 :          0 :                         ctx->speed_mask = 0;
     582                 :            : 
     583                 :            :         } else {
     584         [ #  # ]:          0 :                 if (ctx->lane_idx < 0)
     585                 :          0 :                         ctx->speed_mask = NT_LINK_SPEED_100G;
     586                 :            : 
     587                 :            :                 else
     588                 :          0 :                         ctx->speed_mask = NT_LINK_SPEED_25G;
     589                 :            : 
     590         [ #  # ]:          0 :                 if (qsfp28_is_rate_selection_enabled(ctx)) {
     591                 :            :                         /*
     592                 :            :                          * It is assumed that if the module supports dual rates then the other rate
     593                 :            :                          * is 10G per lane or 40G for all lanes.
     594                 :            :                          */
     595         [ #  # ]:          0 :                         if (ctx->lane_idx < 0)
     596                 :          0 :                                 ctx->speed_mask |= NT_LINK_SPEED_40G;
     597                 :            : 
     598                 :            :                         else
     599                 :          0 :                                 ctx->speed_mask = NT_LINK_SPEED_10G;
     600                 :            :                 }
     601                 :            :         }
     602                 :          0 : }
     603                 :            : 
     604                 :          0 : static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx)
     605                 :            : {
     606                 :            :         uint8_t device_tech;
     607                 :          0 :         read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), &device_tech);
     608                 :            : 
     609         [ #  # ]:          0 :         switch (device_tech & 0xF0) {
     610                 :            :         case 0xA0:      /* Copper cable unequalized */
     611                 :            :                 break;
     612                 :            : 
     613                 :            :         case 0xC0:      /* Copper cable, near and far end limiting active equalizers */
     614                 :            :         case 0xD0:      /* Copper cable, far end limiting active equalizers */
     615                 :            :         case 0xE0:      /* Copper cable, near end limiting active equalizers */
     616                 :            :                 break;
     617                 :            : 
     618                 :          0 :         default:/* Optical */
     619                 :          0 :                 ctx->port_type = NT_PORT_TYPE_QSFP_PLUS;
     620                 :          0 :                 break;
     621                 :            :         }
     622                 :          0 : }
     623                 :            : 
     624                 :            : static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx)
     625                 :            : {
     626                 :          0 :         ctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G : (NT_LINK_SPEED_10G);
     627                 :            : }
     628                 :            : 
     629                 :            : static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx)
     630                 :            : {
     631                 :            :         RTE_ASSERT(lane_idx < 4);
     632                 :          0 :         ctx->specific_u.qsfp.qsfp28 = false;
     633                 :          0 :         ctx->lane_idx = lane_idx;
     634                 :          0 :         ctx->lane_count = 4;
     635                 :            : }
     636                 :            : 
     637                 :          0 : static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
     638                 :            : {
     639                 :            :         qsfpplus_construct(ctx, lane_idx);
     640                 :          0 :         int res = qsfpplus_read_basic_data(ctx);
     641                 :            : 
     642         [ #  # ]:          0 :         if (!res) {
     643                 :          0 :                 qsfpplus_find_port_params(ctx);
     644                 :            : 
     645                 :            :                 /*
     646                 :            :                  * Read if TX_DISABLE has been implemented
     647                 :            :                  * For passive optical modules this is required while it for copper and active
     648                 :            :                  * optical modules is optional. Under all circumstances register 195.4 will
     649                 :            :                  * indicate, if TX_DISABLE has been implemented in register 86.0-3
     650                 :            :                  */
     651                 :            :                 uint8_t value;
     652                 :          0 :                 read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value);
     653                 :            : 
     654                 :          0 :                 ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0;
     655                 :            : 
     656         [ #  # ]:          0 :                 if (ctx->tx_disable)
     657                 :          0 :                         ctx->options |= (1 << NIM_OPTION_TX_DISABLE);
     658                 :            : 
     659                 :            :                 /*
     660                 :            :                  * Previously - considering AFBR-89BRDZ - code tried to establish if a module was
     661                 :            :                  * RxOnly by testing the state of the lasers after reset. Lasers were for this
     662                 :            :                  * module default disabled.
     663                 :            :                  * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was
     664                 :            :                  * decided that this option should not be detected automatically but from PN
     665                 :            :                  */
     666         [ #  # ]:          0 :                 ctx->specific_u.qsfp.rx_only = (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0;
     667                 :            :                 qsfpplus_set_speed_mask(ctx);
     668                 :            :         }
     669                 :            : 
     670                 :          0 :         return res;
     671                 :            : }
     672                 :            : 
     673                 :          0 : static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx)
     674                 :            : {
     675                 :            :         uint8_t data;
     676                 :            :         bool init_complete_flag_present = false;
     677                 :            : 
     678                 :            :         /*
     679                 :            :          * Revision compliance
     680                 :            :          * 7: SFF-8636 Rev 2.5, 2.6 and 2.7
     681                 :            :          * 8: SFF-8636 Rev 2.8, 2.9 and 2.10
     682                 :            :          */
     683                 :          0 :         read_data_lin(ctx, 1, sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance),
     684                 :          0 :                 &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
     685                 :          0 :         NT_LOG(DBG, NTHW, "NIM RevCompliance = %d",
     686                 :            :                 ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);
     687                 :            : 
     688                 :            :         /* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */
     689         [ #  # ]:          0 :         if (ctx->lane_idx > 0)
     690                 :          0 :                 return;
     691                 :            : 
     692         [ #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) {
     693                 :            :                 /* Check if init complete flag is implemented */
     694                 :          0 :                 read_data_lin(ctx, 221, sizeof(data), &data);
     695                 :          0 :                 init_complete_flag_present = (data & (1 << 4)) != 0;
     696                 :            :         }
     697                 :            : 
     698                 :          0 :         NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", init_complete_flag_present);
     699                 :            : 
     700                 :            :         /*
     701                 :            :          * If the init complete flag is not present then wait 500ms that together with 500ms
     702                 :            :          * after reset (in the adapter code) should be enough to read data from upper pages
     703                 :            :          * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been
     704                 :            :          * prone to this when trying to read sensor options using getQsfpOptionsFromData()
     705                 :            :          * Probably because access to the paged address space is required.
     706                 :            :          */
     707         [ #  # ]:          0 :         if (!init_complete_flag_present) {
     708                 :          0 :                 nt_os_wait_usec(500000);
     709                 :          0 :                 return;
     710                 :            :         }
     711                 :            : 
     712                 :            :         /* Otherwise wait for the init complete flag to be set */
     713                 :            :         int count = 0;
     714                 :            : 
     715                 :            :         while (true) {
     716         [ #  # ]:          0 :                 if (count > 10) {    /* 1 s timeout */
     717                 :          0 :                         NT_LOG(WRN, NTHW, "Timeout waiting for module ready");
     718                 :          0 :                         break;
     719                 :            :                 }
     720                 :            : 
     721                 :          0 :                 read_data_lin(ctx, 6, sizeof(data), &data);
     722                 :            : 
     723         [ #  # ]:          0 :                 if (data & 0x01) {
     724                 :          0 :                         NT_LOG(DBG, NTHW, "Module ready after %dms", count * 100);
     725                 :          0 :                         break;
     726                 :            :                 }
     727                 :            : 
     728                 :          0 :                 nt_os_wait_usec(100000);/* 100 ms */
     729                 :          0 :                 count++;
     730                 :            :         }
     731                 :            : }
     732                 :            : 
     733                 :          0 : static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx)
     734                 :            : {
     735                 :          0 :         const char *const nim_list[] = {
     736                 :            :                 "AFBR-89BDDZ",        /* Avago BiDi */
     737                 :            :                 "AFBR-89BRDZ",        /* Avago BiDi, RxOnly */
     738                 :            :                 "FTLC4352RKPL",       /* Finisar QSFP28-LR */
     739                 :            :                 "FTLC4352RHPL",       /* Finisar QSFP28-DR */
     740                 :            :                 "FTLC4352RJPL",       /* Finisar QSFP28-FR */
     741                 :            :                 "SFBR-89BDDZ-CS4",    /* Foxconn, QSFP28 100G/40G BiDi */
     742                 :            :         };
     743                 :            : 
     744         [ #  # ]:          0 :         for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) {
     745         [ #  # ]:          0 :                 if (ctx->prod_no == nim_list[i]) {
     746                 :          0 :                         ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
     747                 :          0 :                         ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = true;
     748                 :          0 :                         NT_LOG(DBG, NTHW, "Found FEC info via PN list");
     749                 :          0 :                         return;
     750                 :            :                 }
     751                 :            :         }
     752                 :            : 
     753                 :            :         /*
     754                 :            :          * For modules not in the list find FEC info via registers
     755                 :            :          * Read if the module has controllable FEC
     756                 :            :          * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing)
     757                 :            :          * (Page 03h, Bytes 224-229)
     758                 :            :          */
     759                 :            :         uint8_t data;
     760                 :            :         uint16_t addr = 227 + 3 * 128;
     761                 :          0 :         read_data_lin(ctx, addr, sizeof(data), &data);
     762                 :            : 
     763                 :            :         /* Check if the module has FEC support that can be controlled */
     764                 :          0 :         ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = (data & (1 << 6)) != 0;
     765                 :          0 :         ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = (data & (1 << 7)) != 0;
     766                 :            : 
     767         [ #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)
     768                 :          0 :                 ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);
     769                 :            : 
     770         [ #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)
     771                 :          0 :                 ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC);
     772                 :            : }
     773                 :            : 
     774                 :          0 : static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)
     775                 :            : {
     776                 :          0 :         int res = qsfpplus_preinit(ctx, lane_idx);
     777                 :            : 
     778         [ #  # ]:          0 :         if (!res) {
     779                 :          0 :                 qsfp28_wait_for_ready_after_reset(ctx);
     780                 :          0 :                 memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0,
     781                 :            :                         sizeof(ctx->specific_u.qsfp.specific_u.qsfp28));
     782                 :          0 :                 ctx->specific_u.qsfp.qsfp28 = true;
     783                 :          0 :                 qsfp28_find_port_params(ctx);
     784                 :          0 :                 qsfp28_get_fec_options(ctx);
     785                 :          0 :                 qsfp28_set_speed_mask(ctx);
     786                 :            :         }
     787                 :            : 
     788                 :          0 :         return res;
     789                 :            : }
     790                 :            : 
     791                 :          0 : int nthw_construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra)
     792                 :            : {
     793                 :          0 :         int res = i2c_nim_common_construct(ctx);
     794                 :            : 
     795      [ #  #  # ]:          0 :         switch (translate_nimid(ctx)) {
     796                 :          0 :         case NT_NIM_QSFP_PLUS:
     797         [ #  # ]:          0 :                 qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
     798                 :          0 :                 break;
     799                 :            : 
     800                 :          0 :         case NT_NIM_QSFP28:
     801         [ #  # ]:          0 :                 qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);
     802                 :          0 :                 break;
     803                 :            : 
     804                 :          0 :         default:
     805                 :            :                 res = 1;
     806                 :          0 :                 NT_LOG(ERR, NTHW, "NIM type %s is not supported", nthw_nim_id_to_text(ctx->nim_id));
     807                 :            :         }
     808                 :            : 
     809                 :          0 :         return res;
     810                 :            : }
     811                 :            : 
     812                 :            : /*
     813                 :            :  * Enables high power according to SFF-8636 Rev 2.7, Table 6-9, Page 35:
     814                 :            :  * When set (= 1b) enables Power Classes 5 to 7 in Byte 129 to exceed 3.5W.
     815                 :            :  * When cleared (= 0b), modules with power classes 5 to 7 must dissipate less
     816                 :            :  * than 3.5W (but are not required to be fully functional). Default 0.
     817                 :            :  */
     818                 :          0 : void nthw_qsfp28_set_high_power(nim_i2c_ctx_p ctx)
     819                 :            : {
     820                 :            :         const uint16_t addr = 93;
     821                 :            :         uint8_t data;
     822                 :            : 
     823                 :            :         /* Enable high power class; Set Page 00h, Byte 93 bit 2 to 1 */
     824                 :          0 :         read_data_lin(ctx, addr, sizeof(data), &data);
     825                 :          0 :         data |= (1 << 2);
     826                 :          0 :         write_data_lin(ctx, addr, sizeof(data), &data);
     827                 :          0 : }
     828                 :            : 
     829                 :            : /*
     830                 :            :  * Enable FEC on media and/or host side. If the operation could be carried out
     831                 :            :  * return true. For some modules media side FEC is enabled but cannot be changed
     832                 :            :  *  while others allow changing the FEC state.
     833                 :            :  * For LR4 modules write operations (which are also not necessary) to the control
     834                 :            :  *  register must be avoided as this introduces I2C errors on NT200A01.
     835                 :            :  */
     836                 :            : 
     837                 :          0 : bool nthw_qsfp28_set_fec_enable(nim_i2c_ctx_p ctx, bool media_side_fec, bool host_side_fec)
     838                 :            : {
     839                 :            :         /*
     840                 :            :          * If the current FEC state does not match the wanted and the FEC cannot be
     841                 :            :          * controlled then the operation cannot be carried out
     842                 :            :          */
     843         [ #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena != media_side_fec &&
     844         [ #  # ]:          0 :                 !ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)
     845                 :            :                 return false;
     846                 :            : 
     847         [ #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ena != host_side_fec &&
     848         [ #  # ]:          0 :                 !ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)
     849                 :            :                 return false;
     850                 :            : 
     851                 :            :         /*
     852                 :            :          * If the current media and host side FEC state matches the wanted states then
     853                 :            :          * no need to do more
     854                 :            :          */
     855   [ #  #  #  # ]:          0 :         if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena == media_side_fec &&
     856                 :            :                 ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ena == host_side_fec)
     857                 :            :                 return true;
     858                 :            : 
     859                 :            :         /*
     860                 :            :          * SFF-8636, Rev 2.10a TABLE 6-29 Optional Channel Control)
     861                 :            :          * (Page 03h, Bytes 230-241)
     862                 :            :          */
     863                 :            :         const uint16_t addr = 230 + 3 * 128;
     864                 :          0 :         uint8_t data = 0;
     865                 :          0 :         read_data_lin(ctx, addr, sizeof(data), &data);
     866                 :            : 
     867         [ #  # ]:          0 :         if (media_side_fec)
     868                 :          0 :                 data &= (uint8_t)(~(1 << 6));
     869                 :            : 
     870                 :            :         else
     871                 :          0 :                 data |= (uint8_t)(1 << 6);
     872                 :            : 
     873         [ #  # ]:          0 :         if (host_side_fec)
     874                 :          0 :                 data |= (uint8_t)(1 << 7);
     875                 :            : 
     876                 :            :         else
     877                 :          0 :                 data &= (uint8_t)(~(1 << 7));
     878                 :            : 
     879                 :          0 :         write_data_lin(ctx, addr, sizeof(data), &data);
     880                 :            :         ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = media_side_fec;
     881                 :          0 :         ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = host_side_fec;
     882                 :          0 :         return true;
     883                 :            : }
     884                 :            : 
     885                 :          0 : void nim_agx_setup(struct nim_i2c_ctx *ctx, nthw_pcal6416a_t *p_io_nim, nthw_i2cm_t *p_nt_i2cm,
     886                 :            :         nthw_pca9849_t *p_ca9849)
     887                 :            : {
     888                 :          0 :         ctx->hwagx.p_nt_i2cm = p_nt_i2cm;
     889                 :          0 :         ctx->hwagx.p_ca9849 = p_ca9849;
     890                 :          0 :         ctx->hwagx.p_io_nim = p_io_nim;
     891                 :          0 : }
     892                 :            : 
     893                 :          0 : static void nim_agx_read(struct nim_i2c_ctx *ctx,
     894                 :            :         uint8_t dev_addr,
     895                 :            :         uint8_t reg_addr,
     896                 :            :         uint8_t data_len,
     897                 :            :         void *p_data)
     898                 :            : {
     899                 :          0 :         nthw_i2cm_t *p_nt_i2cm = ctx->hwagx.p_nt_i2cm;
     900                 :          0 :         nthw_pca9849_t *p_ca9849 = ctx->hwagx.p_ca9849;
     901                 :            :         uint8_t *data = (uint8_t *)p_data;
     902                 :            : 
     903                 :          0 :         rte_spinlock_lock(&p_nt_i2cm->i2cmmutex);
     904                 :          0 :         nthw_pca9849_set_channel(p_ca9849, ctx->hwagx.mux_channel);
     905                 :            : 
     906         [ #  # ]:          0 :         for (uint8_t i = 0; i < data_len; i++) {
     907                 :          0 :                 nthw_i2cm_read(p_nt_i2cm, (uint8_t)(dev_addr >> 1), (uint8_t)(reg_addr + i), data);
     908                 :          0 :                 data++;
     909                 :            :         }
     910                 :            : 
     911                 :            :         rte_spinlock_unlock(&p_nt_i2cm->i2cmmutex);
     912                 :          0 : }
     913                 :            : 
     914                 :          0 : static void nim_agx_write(struct nim_i2c_ctx *ctx,
     915                 :            :         uint8_t dev_addr,
     916                 :            :         uint8_t reg_addr,
     917                 :            :         uint8_t data_len,
     918                 :            :         void *p_data)
     919                 :            : {
     920                 :          0 :         nthw_i2cm_t *p_nt_i2cm = ctx->hwagx.p_nt_i2cm;
     921                 :          0 :         nthw_pca9849_t *p_ca9849 = ctx->hwagx.p_ca9849;
     922                 :            :         uint8_t *data = (uint8_t *)p_data;
     923                 :            : 
     924                 :          0 :         rte_spinlock_lock(&p_nt_i2cm->i2cmmutex);
     925                 :          0 :         nthw_pca9849_set_channel(p_ca9849, ctx->hwagx.mux_channel);
     926                 :            : 
     927         [ #  # ]:          0 :         for (uint8_t i = 0; i < data_len; i++) {
     928                 :          0 :                 nthw_i2cm_write(p_nt_i2cm, (uint8_t)(dev_addr >> 1), (uint8_t)(reg_addr + i),
     929                 :          0 :                         *data++);
     930                 :            :         }
     931                 :            : 
     932                 :            :         rte_spinlock_unlock(&p_nt_i2cm->i2cmmutex);
     933                 :          0 : }
     934                 :            : 
     935                 :          0 : int nim_agx_read_id(struct nim_i2c_ctx *ctx)
     936                 :            : {
     937                 :          0 :         nim_agx_read(ctx, 0xA0, 0, sizeof(ctx->nim_id), &ctx->nim_id);
     938                 :          0 :         return 0;
     939                 :            : }

Generated by: LCOV version 1.14