326 lines
9.7 KiB
C
326 lines
9.7 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause */
|
|
/* Copyright (c) 2023, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. 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.
|
|
*
|
|
* 3. Neither the name of the Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* 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 "ice_common.h"
|
|
|
|
#include "ice_ddp_common.h"
|
|
/**
|
|
* ice_pkg_get_supported_vlan_mode - chk if DDP supports Double VLAN mode (DVM)
|
|
* @hw: pointer to the HW struct
|
|
* @dvm: output variable to determine if DDP supports DVM(true) or SVM(false)
|
|
*/
|
|
static enum ice_status
|
|
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;
|
|
enum ice_status 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 ICE_ERR_NO_MEMORY;
|
|
|
|
/* 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) {
|
|
ice_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[0].bm[i]);
|
|
|
|
ice_bitmap_from_array32(entry, arr, (u16)ICE_META_INIT_BITS);
|
|
|
|
/* check if DVM is supported */
|
|
*dvm = ice_is_bit_set(entry, ICE_META_VLAN_MODE_BIT);
|
|
}
|
|
|
|
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 enum ice_status
|
|
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 ICE_ERR_PARAM;
|
|
|
|
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 };
|
|
enum ice_status 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)
|
|
{
|
|
enum ice_status status;
|
|
bool pkg_supports_dvm;
|
|
|
|
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 };
|
|
enum ice_status 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;
|
|
}
|
|
|
|
/**
|
|
* 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 enum ice_status
|
|
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 ICE_ERR_PARAM;
|
|
|
|
if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX)
|
|
return ICE_ERR_PARAM;
|
|
|
|
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 ICE_ERR_PARAM;
|
|
|
|
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 ICE_ERR_PARAM;
|
|
|
|
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_svm - set single VLAN mode
|
|
* @hw: pointer to the HW structure
|
|
*/
|
|
static enum ice_status ice_set_svm(struct ice_hw *hw)
|
|
{
|
|
struct ice_aqc_set_vlan_mode *set_params;
|
|
enum ice_status status;
|
|
|
|
status = ice_aq_set_port_params(hw->port_info, 0, false, false, false, NULL);
|
|
if (status) {
|
|
ice_debug(hw, ICE_DBG_INIT, "Failed to set port parameters for single VLAN mode\n");
|
|
return status;
|
|
}
|
|
|
|
set_params = (struct ice_aqc_set_vlan_mode *)
|
|
ice_malloc(hw, sizeof(*set_params));
|
|
if (!set_params)
|
|
return ICE_ERR_NO_MEMORY;
|
|
|
|
/* 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");
|
|
|
|
ice_free(hw, set_params);
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* ice_set_vlan_mode
|
|
* @hw: pointer to the HW structure
|
|
*/
|
|
enum ice_status ice_set_vlan_mode(struct ice_hw *hw)
|
|
{
|
|
if (!ice_is_dvm_supported(hw))
|
|
return ICE_SUCCESS;
|
|
|
|
return ice_set_svm(hw);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|