Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2019-2021, Intel Corporation. */ #include "ice_common.h" /** * ice_pkg_get_supported_vlan_mode - determine if DDP supports Double VLAN mode * @hw: pointer to the HW struct * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false) */ static int ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm) { u16 meta_init_size = sizeof(struct ice_meta_init_section); struct ice_meta_init_section *sect; struct ice_buf_build *bld; int status; /* if anything fails, we assume there is no DVM support */ *dvm = false; bld = ice_pkg_buf_alloc_single_section(hw, ICE_SID_RXPARSER_METADATA_INIT, meta_init_size, (void **)§); if (!bld) return -ENOMEM; /* only need to read a single section */ sect->count = cpu_to_le16(1); sect->offset = cpu_to_le16(ICE_META_VLAN_MODE_ENTRY); status = ice_aq_upload_section(hw, (struct ice_buf_hdr *)ice_pkg_buf(bld), ICE_PKG_BUF_SIZE, NULL); if (!status) { DECLARE_BITMAP(entry, ICE_META_INIT_BITS); u32 arr[ICE_META_INIT_DW_CNT]; u16 i; /* convert to host bitmap format */ for (i = 0; i < ICE_META_INIT_DW_CNT; i++) arr[i] = le32_to_cpu(sect->entry.bm[i]); bitmap_from_arr32(entry, arr, (u16)ICE_META_INIT_BITS); /* check if DVM is supported */ *dvm = test_bit(ICE_META_VLAN_MODE_BIT, entry); } ice_pkg_buf_free(hw, bld); return status; } /** * ice_aq_get_vlan_mode - get the VLAN mode of the device * @hw: pointer to the HW structure * @get_params: structure FW fills in based on the current VLAN mode config * * Get VLAN Mode Parameters (0x020D) */ static int ice_aq_get_vlan_mode(struct ice_hw *hw, struct ice_aqc_get_vlan_mode *get_params) { struct ice_aq_desc desc; if (!get_params) return -EINVAL; ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_vlan_mode_parameters); return ice_aq_send_cmd(hw, &desc, get_params, sizeof(*get_params), NULL); } /** * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled * @hw: pointer to the HW structure * * Returns true if the hardware/firmware is configured in double VLAN mode, * else return false signaling that the hardware/firmware is configured in * single VLAN mode. * * Also, return false if this call fails for any reason (i.e. firmware doesn't * support this AQ call). */ static bool ice_aq_is_dvm_ena(struct ice_hw *hw) { struct ice_aqc_get_vlan_mode get_params = { 0 }; int status; status = ice_aq_get_vlan_mode(hw, &get_params); if (status) { ice_debug(hw, ICE_DBG_AQ, "Failed to get VLAN mode, status %d\n", status); return false; } return (get_params.vlan_mode & ICE_AQ_VLAN_MODE_DVM_ENA); } /** * ice_is_dvm_ena - check if double VLAN mode is enabled * @hw: pointer to the HW structure * * The device is configured in single or double VLAN mode on initialization and * this cannot be dynamically changed during runtime. Based on this there is no * need to make an AQ call every time the driver needs to know the VLAN mode. * Instead, use the cached VLAN mode. */ bool ice_is_dvm_ena(struct ice_hw *hw) { return hw->dvm_ena; } /** * ice_cache_vlan_mode - cache VLAN mode after DDP is downloaded * @hw: pointer to the HW structure * * This is only called after downloading the DDP and after the global * configuration lock has been released because all ports on a device need to * cache the VLAN mode. */ static void ice_cache_vlan_mode(struct ice_hw *hw) { hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false; } /** * ice_pkg_supports_dvm - find out if DDP supports DVM * @hw: pointer to the HW structure */ static bool ice_pkg_supports_dvm(struct ice_hw *hw) { bool pkg_supports_dvm; int status; status = ice_pkg_get_supported_vlan_mode(hw, &pkg_supports_dvm); if (status) { ice_debug(hw, ICE_DBG_PKG, "Failed to get supported VLAN mode, status %d\n", status); return false; } return pkg_supports_dvm; } /** * ice_fw_supports_dvm - find out if FW supports DVM * @hw: pointer to the HW structure */ static bool ice_fw_supports_dvm(struct ice_hw *hw) { struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; int status; /* If firmware returns success, then it supports DVM, else it only * supports SVM */ status = ice_aq_get_vlan_mode(hw, &get_vlan_mode); if (status) { ice_debug(hw, ICE_DBG_NVM, "Failed to get VLAN mode, status %d\n", status); return false; } return true; } /** * ice_is_dvm_supported - check if Double VLAN Mode is supported * @hw: pointer to the hardware structure * * Returns true if Double VLAN Mode (DVM) is supported and false if only Single * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and * firmware must support it, otherwise only SVM is supported. This function * should only be called while the global config lock is held and after the * package has been successfully downloaded. */ static bool ice_is_dvm_supported(struct ice_hw *hw) { if (!ice_pkg_supports_dvm(hw)) { ice_debug(hw, ICE_DBG_PKG, "DDP doesn't support DVM\n"); return false; } if (!ice_fw_supports_dvm(hw)) { ice_debug(hw, ICE_DBG_PKG, "FW doesn't support DVM\n"); return false; } return true; } #define ICE_EXTERNAL_VLAN_ID_FV_IDX 11 #define ICE_SW_LKUP_VLAN_LOC_LKUP_IDX 1 #define ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX 2 #define ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX 2 #define ICE_PKT_FLAGS_0_TO_15_FV_IDX 1 static struct ice_update_recipe_lkup_idx_params ice_dvm_dflt_recipes[] = { { /* Update recipe ICE_SW_LKUP_VLAN to filter based on the * outer/single VLAN in DVM */ .rid = ICE_SW_LKUP_VLAN, .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, .ignore_valid = true, .mask = 0, .mask_valid = false, /* use pre-existing mask */ .lkup_idx = ICE_SW_LKUP_VLAN_LOC_LKUP_IDX, }, { /* Update recipe ICE_SW_LKUP_VLAN to filter based on the VLAN * packet flags to support VLAN filtering on multiple VLAN * ethertypes (i.e. 0x8100 and 0x88a8) in DVM */ .rid = ICE_SW_LKUP_VLAN, .fv_idx = ICE_PKT_FLAGS_0_TO_15_FV_IDX, .ignore_valid = false, .mask = ICE_PKT_VLAN_MASK, .mask_valid = true, .lkup_idx = ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX, }, { /* Update recipe ICE_SW_LKUP_PROMISC_VLAN to filter based on the * outer/single VLAN in DVM */ .rid = ICE_SW_LKUP_PROMISC_VLAN, .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, .ignore_valid = true, .mask = 0, .mask_valid = false, /* use pre-existing mask */ .lkup_idx = ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX, }, }; /** * ice_dvm_update_dflt_recipes - update default switch recipes in DVM * @hw: hardware structure used to update the recipes */ static int ice_dvm_update_dflt_recipes(struct ice_hw *hw) { unsigned long i; for (i = 0; i < ARRAY_SIZE(ice_dvm_dflt_recipes); i++) { struct ice_update_recipe_lkup_idx_params *params; int status; params = &ice_dvm_dflt_recipes[i]; status = ice_update_recipe_lkup_idx(hw, params); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to update RID %d lkup_idx %d fv_idx %d mask_valid %s mask 0x%04x\n", params->rid, params->lkup_idx, params->fv_idx, params->mask_valid ? "true" : "false", params->mask); return status; } } return 0; } /** * ice_aq_set_vlan_mode - set the VLAN mode of the device * @hw: pointer to the HW structure * @set_params: requested VLAN mode configuration * * Set VLAN Mode Parameters (0x020C) */ static int ice_aq_set_vlan_mode(struct ice_hw *hw, struct ice_aqc_set_vlan_mode *set_params) { u8 rdma_packet, mng_vlan_prot_id; struct ice_aq_desc desc; if (!set_params) return -EINVAL; if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX) return -EINVAL; rdma_packet = set_params->rdma_packet; if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING && rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING) return -EINVAL; mng_vlan_prot_id = set_params->mng_vlan_prot_id; if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER && mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER) return -EINVAL; ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_vlan_mode_parameters); desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); return ice_aq_send_cmd(hw, &desc, set_params, sizeof(*set_params), NULL); } /** * ice_set_dvm - sets up software and hardware for double VLAN mode * @hw: pointer to the hardware structure */ static int ice_set_dvm(struct ice_hw *hw) { struct ice_aqc_set_vlan_mode params = { 0 }; int status; params.l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_OUTER_CTAG; params.rdma_packet = ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING; params.mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER; status = ice_aq_set_vlan_mode(hw, ¶ms); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to set double VLAN mode parameters, status %d\n", status); return status; } status = ice_dvm_update_dflt_recipes(hw); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to update default recipes for double VLAN mode, status %d\n", status); return status; } status = ice_aq_set_port_params(hw->port_info, true, NULL); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to set port in double VLAN mode, status %d\n", status); return status; } status = ice_set_dvm_boost_entries(hw); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to set boost TCAM entries for double VLAN mode, status %d\n", status); return status; } return 0; } /** * ice_set_svm - set single VLAN mode * @hw: pointer to the HW structure */ static int ice_set_svm(struct ice_hw *hw) { struct ice_aqc_set_vlan_mode *set_params; int status; status = ice_aq_set_port_params(hw->port_info, false, NULL); if (status) { ice_debug(hw, ICE_DBG_INIT, "Failed to set port parameters for single VLAN mode\n"); return status; } set_params = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*set_params), GFP_KERNEL); if (!set_params) return -ENOMEM; /* default configuration for SVM configurations */ set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG; set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING; set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER; status = ice_aq_set_vlan_mode(hw, set_params); if (status) ice_debug(hw, ICE_DBG_INIT, "Failed to configure port in single VLAN mode\n"); devm_kfree(ice_hw_to_dev(hw), set_params); return status; } /** * ice_set_vlan_mode * @hw: pointer to the HW structure */ int ice_set_vlan_mode(struct ice_hw *hw) { if (!ice_is_dvm_supported(hw)) return 0; if (!ice_set_dvm(hw)) return 0; return ice_set_svm(hw); } /** * ice_print_dvm_not_supported - print if DDP and/or FW doesn't support DVM * @hw: pointer to the HW structure * * The purpose of this function is to print that QinQ is not supported due to * incompatibilty from the DDP and/or FW. This will give a hint to the user to * update one and/or both components if they expect QinQ functionality. */ static void ice_print_dvm_not_supported(struct ice_hw *hw) { bool pkg_supports_dvm = ice_pkg_supports_dvm(hw); bool fw_supports_dvm = ice_fw_supports_dvm(hw); if (!fw_supports_dvm && !pkg_supports_dvm) dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package and NVM to versions that support QinQ.\n"); else if (!pkg_supports_dvm) dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package to a version that supports QinQ.\n"); else if (!fw_supports_dvm) dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your NVM to a version that supports QinQ.\n"); } /** * ice_post_pkg_dwnld_vlan_mode_cfg - configure VLAN mode after DDP download * @hw: pointer to the HW structure * * This function is meant to configure any VLAN mode specific functionality * after the global configuration lock has been released and the DDP has been * downloaded. * * Since only one PF downloads the DDP and configures the VLAN mode there needs * to be a way to configure the other PFs after the DDP has been downloaded and * the global configuration lock has been released. All such code should go in * this function. */ void ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw) { ice_cache_vlan_mode(hw); if (ice_is_dvm_ena(hw)) ice_change_proto_id_to_dvm(); else ice_print_dvm_not_supported(hw); } |