1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/err.h> 6 #include <linux/sfp.h> 7 8 #include "core.h" 9 #include "core_env.h" 10 #include "item.h" 11 #include "reg.h" 12 13 static int mlxsw_env_validate_cable_ident(struct mlxsw_core *core, int id, 14 bool *qsfp) 15 { 16 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 17 char mcia_pl[MLXSW_REG_MCIA_LEN]; 18 u8 ident; 19 int err; 20 21 mlxsw_reg_mcia_pack(mcia_pl, id, 0, MLXSW_REG_MCIA_PAGE0_LO_OFF, 0, 1, 22 MLXSW_REG_MCIA_I2C_ADDR_LOW); 23 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl); 24 if (err) 25 return err; 26 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 27 ident = eeprom_tmp[0]; 28 switch (ident) { 29 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP: 30 *qsfp = false; 31 break; 32 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: /* fall-through */ 33 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */ 34 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: /* fall-through */ 35 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_DD: 36 *qsfp = true; 37 break; 38 default: 39 return -EINVAL; 40 } 41 42 return 0; 43 } 44 45 static int 46 mlxsw_env_query_module_eeprom(struct mlxsw_core *mlxsw_core, int module, 47 u16 offset, u16 size, void *data, 48 unsigned int *p_read_size) 49 { 50 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 51 char mcia_pl[MLXSW_REG_MCIA_LEN]; 52 u16 i2c_addr; 53 int status; 54 int err; 55 56 size = min_t(u16, size, MLXSW_REG_MCIA_EEPROM_SIZE); 57 58 if (offset < MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH && 59 offset + size > MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) 60 /* Cross pages read, read until offset 256 in low page */ 61 size = MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH - offset; 62 63 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_LOW; 64 if (offset >= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH) { 65 i2c_addr = MLXSW_REG_MCIA_I2C_ADDR_HIGH; 66 offset -= MLXSW_REG_MCIA_EEPROM_PAGE_LENGTH; 67 } 68 69 mlxsw_reg_mcia_pack(mcia_pl, module, 0, 0, offset, size, i2c_addr); 70 71 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl); 72 if (err) 73 return err; 74 75 status = mlxsw_reg_mcia_status_get(mcia_pl); 76 if (status) 77 return -EIO; 78 79 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 80 memcpy(data, eeprom_tmp, size); 81 *p_read_size = size; 82 83 return 0; 84 } 85 86 int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module, 87 int off, int *temp) 88 { 89 char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; 90 union { 91 u8 buf[MLXSW_REG_MCIA_TH_ITEM_SIZE]; 92 u16 temp; 93 } temp_thresh; 94 char mcia_pl[MLXSW_REG_MCIA_LEN] = {0}; 95 char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0}; 96 u16 module_temp; 97 bool qsfp; 98 int err; 99 100 mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module, 101 1); 102 err = mlxsw_reg_query(core, MLXSW_REG(mtbr), mtbr_pl); 103 if (err) 104 return err; 105 106 /* Don't read temperature thresholds for module with no valid info. */ 107 mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &module_temp, NULL); 108 switch (module_temp) { 109 case MLXSW_REG_MTBR_BAD_SENS_INFO: /* fall-through */ 110 case MLXSW_REG_MTBR_NO_CONN: /* fall-through */ 111 case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */ 112 case MLXSW_REG_MTBR_INDEX_NA: 113 *temp = 0; 114 return 0; 115 default: 116 /* Do not consider thresholds for zero temperature. */ 117 if (MLXSW_REG_MTMP_TEMP_TO_MC(module_temp) == 0) { 118 *temp = 0; 119 return 0; 120 } 121 break; 122 } 123 124 /* Read Free Side Device Temperature Thresholds from page 03h 125 * (MSB at lower byte address). 126 * Bytes: 127 * 128-129 - Temp High Alarm (SFP_TEMP_HIGH_ALARM); 128 * 130-131 - Temp Low Alarm (SFP_TEMP_LOW_ALARM); 129 * 132-133 - Temp High Warning (SFP_TEMP_HIGH_WARN); 130 * 134-135 - Temp Low Warning (SFP_TEMP_LOW_WARN); 131 */ 132 133 /* Validate module identifier value. */ 134 err = mlxsw_env_validate_cable_ident(core, module, &qsfp); 135 if (err) 136 return err; 137 138 if (qsfp) 139 mlxsw_reg_mcia_pack(mcia_pl, module, 0, 140 MLXSW_REG_MCIA_TH_PAGE_NUM, 141 MLXSW_REG_MCIA_TH_PAGE_OFF + off, 142 MLXSW_REG_MCIA_TH_ITEM_SIZE, 143 MLXSW_REG_MCIA_I2C_ADDR_LOW); 144 else 145 mlxsw_reg_mcia_pack(mcia_pl, module, 0, 146 MLXSW_REG_MCIA_PAGE0_LO, 147 off, MLXSW_REG_MCIA_TH_ITEM_SIZE, 148 MLXSW_REG_MCIA_I2C_ADDR_HIGH); 149 150 err = mlxsw_reg_query(core, MLXSW_REG(mcia), mcia_pl); 151 if (err) 152 return err; 153 154 mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); 155 memcpy(temp_thresh.buf, eeprom_tmp, MLXSW_REG_MCIA_TH_ITEM_SIZE); 156 *temp = temp_thresh.temp * 1000; 157 158 return 0; 159 } 160 161 int mlxsw_env_get_module_info(struct mlxsw_core *mlxsw_core, int module, 162 struct ethtool_modinfo *modinfo) 163 { 164 u8 module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE]; 165 u16 offset = MLXSW_REG_MCIA_EEPROM_MODULE_INFO_SIZE; 166 u8 module_rev_id, module_id, diag_mon; 167 unsigned int read_size; 168 int err; 169 170 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 0, offset, 171 module_info, &read_size); 172 if (err) 173 return err; 174 175 if (read_size < offset) 176 return -EIO; 177 178 module_rev_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID]; 179 module_id = module_info[MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID]; 180 181 switch (module_id) { 182 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP: 183 modinfo->type = ETH_MODULE_SFF_8436; 184 modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 185 break; 186 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP_PLUS: /* fall-through */ 187 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28: 188 if (module_id == MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_QSFP28 || 189 module_rev_id >= 190 MLXSW_REG_MCIA_EEPROM_MODULE_INFO_REV_ID_8636) { 191 modinfo->type = ETH_MODULE_SFF_8636; 192 modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; 193 } else { 194 modinfo->type = ETH_MODULE_SFF_8436; 195 modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 196 } 197 break; 198 case MLXSW_REG_MCIA_EEPROM_MODULE_INFO_ID_SFP: 199 /* Verify if transceiver provides diagnostic monitoring page */ 200 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, 201 SFP_DIAGMON, 1, &diag_mon, 202 &read_size); 203 if (err) 204 return err; 205 206 if (read_size < 1) 207 return -EIO; 208 209 modinfo->type = ETH_MODULE_SFF_8472; 210 if (diag_mon) 211 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 212 else 213 modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN / 2; 214 break; 215 default: 216 return -EINVAL; 217 } 218 219 return 0; 220 } 221 EXPORT_SYMBOL(mlxsw_env_get_module_info); 222 223 int mlxsw_env_get_module_eeprom(struct net_device *netdev, 224 struct mlxsw_core *mlxsw_core, int module, 225 struct ethtool_eeprom *ee, u8 *data) 226 { 227 int offset = ee->offset; 228 unsigned int read_size; 229 int i = 0; 230 int err; 231 232 if (!ee->len) 233 return -EINVAL; 234 235 memset(data, 0, ee->len); 236 237 while (i < ee->len) { 238 err = mlxsw_env_query_module_eeprom(mlxsw_core, module, offset, 239 ee->len - i, data + i, 240 &read_size); 241 if (err) { 242 netdev_err(netdev, "Eeprom query failed\n"); 243 return err; 244 } 245 246 i += read_size; 247 offset += read_size; 248 } 249 250 return 0; 251 } 252 EXPORT_SYMBOL(mlxsw_env_get_module_eeprom); 253