Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ice/ice_vlan_mode.c
Show All 24 Lines | |||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | * 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 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
* POSSIBILITY OF SUCH DAMAGE. | * POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
/*$FreeBSD$*/ | /*$FreeBSD$*/ | ||||
#include "ice_vlan_mode.h" | |||||
#include "ice_common.h" | #include "ice_common.h" | ||||
/** | /** | ||||
* ice_set_svm - set single VLAN mode | * ice_pkg_supports_dvm - determine 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 | * @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_set_svm_dflt(struct ice_hw *hw) | static enum ice_status | ||||
ice_aq_get_vlan_mode(struct ice_hw *hw, | |||||
struct ice_aqc_get_vlan_mode *get_params) | |||||
{ | { | ||||
ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__); | struct ice_aq_desc desc; | ||||
return ice_aq_set_port_params(hw->port_info, 0, false, false, false, NULL); | 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_init_vlan_mode_ops - initialize VLAN mode configuration ops | * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled | ||||
* @hw: pointer to the HW structure | * @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). | |||||
*/ | */ | ||||
void ice_init_vlan_mode_ops(struct ice_hw *hw) | static bool ice_aq_is_dvm_ena(struct ice_hw *hw) | ||||
{ | { | ||||
hw->vlan_mode_ops.set_dvm = NULL; | struct ice_aqc_get_vlan_mode get_params = { 0 }; | ||||
hw->vlan_mode_ops.set_svm = ice_set_svm_dflt; | 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_set_vlan_mode | * ice_is_dvm_ena - check if double VLAN mode is enabled | ||||
* @hw: pointer to the HW structure | * @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. | |||||
*/ | */ | ||||
enum ice_status ice_set_vlan_mode(struct ice_hw *hw) | bool ice_is_dvm_ena(struct ice_hw *hw) | ||||
{ | { | ||||
enum ice_status status = ICE_ERR_NOT_IMPL; | return hw->dvm_ena; | ||||
} | |||||
if (hw->vlan_mode_ops.set_dvm) | /** | ||||
status = hw->vlan_mode_ops.set_dvm(hw); | * 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. | |||||
*/ | |||||
void ice_cache_vlan_mode(struct ice_hw *hw) | |||||
{ | |||||
hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false; | |||||
} | |||||
/** | |||||
* 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) | |||||
{ | |||||
struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; | |||||
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; | |||||
} | |||||
if (!pkg_supports_dvm) | |||||
return false; | |||||
/* 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_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) | if (status) | ||||
return hw->vlan_mode_ops.set_svm(hw); | ice_debug(hw, ICE_DBG_INIT, "Failed to configure port in single VLAN mode\n"); | ||||
ice_free(hw, set_params); | |||||
return status; | 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); | |||||
} | } |