// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2013 - 2021 Intel Corporation. */
#include <linux/avf/virtchnl.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "i40e_adminq_cmd.h"
#include "i40e_devids.h"
#include "i40e_prototype.h"
#include "i40e_register.h"
/**
* i40e_set_mac_type - Sets MAC type
* @ hw : pointer to the HW structure
*
* This function sets the mac type of the adapter based on the
* vendor ID and device ID stored in the hw structure .
**/
int i40e_set_mac_type(struct i40e_hw *hw)
{
int status = 0 ;
if (hw->vendor_id == PCI_VENDOR_ID_INTEL) {
switch (hw->device_id) {
case I40E_DEV_ID_SFP_XL710:
case I40E_DEV_ID_QEMU:
case I40E_DEV_ID_KX_B:
case I40E_DEV_ID_KX_C:
case I40E_DEV_ID_QSFP_A:
case I40E_DEV_ID_QSFP_B:
case I40E_DEV_ID_QSFP_C:
case I40E_DEV_ID_1G_BASE_T_BC:
case I40E_DEV_ID_5G_BASE_T_BC:
case I40E_DEV_ID_10G_BASE_T:
case I40E_DEV_ID_10G_BASE_T4:
case I40E_DEV_ID_10G_BASE_T_BC:
case I40E_DEV_ID_10G_B:
case I40E_DEV_ID_10G_SFP:
case I40E_DEV_ID_20G_KR2:
case I40E_DEV_ID_20G_KR2_A:
case I40E_DEV_ID_25G_B:
case I40E_DEV_ID_25G_SFP28:
case I40E_DEV_ID_X710_N3000:
case I40E_DEV_ID_XXV710_N3000:
hw->mac.type = I40E_MAC_XL710;
break ;
case I40E_DEV_ID_KX_X722:
case I40E_DEV_ID_QSFP_X722:
case I40E_DEV_ID_SFP_X722:
case I40E_DEV_ID_1G_BASE_T_X722:
case I40E_DEV_ID_10G_BASE_T_X722:
case I40E_DEV_ID_SFP_I_X722:
case I40E_DEV_ID_SFP_X722_A:
hw->mac.type = I40E_MAC_X722;
break ;
default :
hw->mac.type = I40E_MAC_GENERIC;
break ;
}
} else {
status = -ENODEV;
}
hw_dbg(hw, "i40e_set_mac_type found mac: %d, returns: %d\n" ,
hw->mac.type, status);
return status;
}
/**
* i40e_debug_aq
* @ hw : debug mask related to admin queue
* @ mask : debug mask
* @ desc : pointer to admin queue descriptor
* @ buffer : pointer to command buffer
* @ buf_len : max length of buffer
*
* Dumps debug log about adminq command with descriptor contents .
**/
void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
void *buffer, u16 buf_len)
{
struct libie_aq_desc *aq_desc = (struct libie_aq_desc *)desc;
u32 effective_mask = hw->debug_mask & mask;
char prefix[27 ];
u16 len;
u8 *buf = (u8 *)buffer;
if (!effective_mask || !desc)
return ;
len = le16_to_cpu(aq_desc->datalen);
i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR,
"AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n" ,
le16_to_cpu(aq_desc->opcode),
le16_to_cpu(aq_desc->flags),
le16_to_cpu(aq_desc->datalen),
le16_to_cpu(aq_desc->retval));
i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR,
"\tcookie (h,l) 0x%08X 0x%08X\n" ,
le32_to_cpu(aq_desc->cookie_high),
le32_to_cpu(aq_desc->cookie_low));
i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR,
"\tparam (0,1) 0x%08X 0x%08X\n" ,
le32_to_cpu(aq_desc->params.generic.param0),
le32_to_cpu(aq_desc->params.generic.param1));
i40e_debug(hw, mask & I40E_DEBUG_AQ_DESCRIPTOR,
"\taddr (h,l) 0x%08X 0x%08X\n" ,
le32_to_cpu(aq_desc->params.generic.addr_high),
le32_to_cpu(aq_desc->params.generic.addr_low));
if (buffer && buf_len != 0 && len != 0 &&
(effective_mask & I40E_DEBUG_AQ_DESC_BUFFER)) {
i40e_debug(hw, mask, "AQ CMD Buffer:\n" );
if (buf_len < len)
len = buf_len;
snprintf(prefix, sizeof (prefix),
"i40e %02x:%02x.%x: \t0x" ,
hw->bus.bus_id,
hw->bus.device,
hw->bus.func);
print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET,
16 , 1 , buf, len, false );
}
}
/**
* i40e_check_asq_alive
* @ hw : pointer to the hw struct
*
* Returns true if Queue is enabled else false .
**/
bool i40e_check_asq_alive(struct i40e_hw *hw)
{
/* Check if the queue is initialized */
if (!hw->aq.asq.count)
return false ;
return !!(rd32(hw, I40E_PF_ATQLEN) & I40E_PF_ATQLEN_ATQENABLE_MASK);
}
/**
* i40e_aq_queue_shutdown
* @ hw : pointer to the hw struct
* @ unloading : is the driver unloading itself
*
* Tell the Firmware that we ' re shutting down the AdminQ and whether
* or not the driver is unloading as well .
**/
int i40e_aq_queue_shutdown(struct i40e_hw *hw,
bool unloading)
{
struct i40e_aqc_queue_shutdown *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_queue_shutdown);
cmd = libie_aq_raw(&desc);
if (unloading)
cmd->driver_unloading = cpu_to_le32(I40E_AQ_DRIVER_UNLOADING);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , NULL);
return status;
}
/**
* i40e_aq_get_set_rss_lut
* @ hw : pointer to the hardware structure
* @ vsi_id : vsi fw index
* @ pf_lut : for PF table set true , for VSI table set false
* @ lut : pointer to the lut buffer provided by the caller
* @ lut_size : size of the lut buffer
* @ set : set true to set the table , false to get the table
*
* Internal function to get or set RSS look up table
**/
static int i40e_aq_get_set_rss_lut(struct i40e_hw *hw,
u16 vsi_id, bool pf_lut,
u8 *lut, u16 lut_size,
bool set)
{
struct i40e_aqc_get_set_rss_lut *cmd_resp;
struct libie_aq_desc desc;
int status;
u16 flags;
if (set)
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_rss_lut);
else
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_rss_lut);
cmd_resp = libie_aq_raw(&desc);
/* Indirect command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_RD);
vsi_id = FIELD_PREP(I40E_AQC_SET_RSS_LUT_VSI_ID_MASK, vsi_id) |
FIELD_PREP(I40E_AQC_SET_RSS_LUT_VSI_VALID, 1 );
cmd_resp->vsi_id = cpu_to_le16(vsi_id);
if (pf_lut)
flags = FIELD_PREP(I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK,
I40E_AQC_SET_RSS_LUT_TABLE_TYPE_PF);
else
flags = FIELD_PREP(I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK,
I40E_AQC_SET_RSS_LUT_TABLE_TYPE_VSI);
cmd_resp->flags = cpu_to_le16(flags);
status = i40e_asq_send_command(hw, &desc, lut, lut_size, NULL);
return status;
}
/**
* i40e_aq_get_rss_lut
* @ hw : pointer to the hardware structure
* @ vsi_id : vsi fw index
* @ pf_lut : for PF table set true , for VSI table set false
* @ lut : pointer to the lut buffer provided by the caller
* @ lut_size : size of the lut buffer
*
* get the RSS lookup table , PF or VSI type
**/
int i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 vsi_id,
bool pf_lut, u8 *lut, u16 lut_size)
{
return i40e_aq_get_set_rss_lut(hw, vsi_id, pf_lut, lut, lut_size,
false );
}
/**
* i40e_aq_set_rss_lut
* @ hw : pointer to the hardware structure
* @ vsi_id : vsi fw index
* @ pf_lut : for PF table set true , for VSI table set false
* @ lut : pointer to the lut buffer provided by the caller
* @ lut_size : size of the lut buffer
*
* set the RSS lookup table , PF or VSI type
**/
int i40e_aq_set_rss_lut(struct i40e_hw *hw, u16 vsi_id,
bool pf_lut, u8 *lut, u16 lut_size)
{
return i40e_aq_get_set_rss_lut(hw, vsi_id, pf_lut, lut, lut_size, true );
}
/**
* i40e_aq_get_set_rss_key
* @ hw : pointer to the hw struct
* @ vsi_id : vsi fw index
* @ key : pointer to key info struct
* @ set : set true to set the key , false to get the key
*
* get the RSS key per VSI
**/
static int i40e_aq_get_set_rss_key(struct i40e_hw *hw,
u16 vsi_id,
struct i40e_aqc_get_set_rss_key_data *key,
bool set)
{
u16 key_size = sizeof (struct i40e_aqc_get_set_rss_key_data);
struct i40e_aqc_get_set_rss_key *cmd_resp;
struct libie_aq_desc desc;
int status;
if (set)
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_rss_key);
else
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_rss_key);
cmd_resp = libie_aq_raw(&desc);
/* Indirect command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_RD);
vsi_id = FIELD_PREP(I40E_AQC_SET_RSS_KEY_VSI_ID_MASK, vsi_id) |
FIELD_PREP(I40E_AQC_SET_RSS_KEY_VSI_VALID, 1 );
cmd_resp->vsi_id = cpu_to_le16(vsi_id);
status = i40e_asq_send_command(hw, &desc, key, key_size, NULL);
return status;
}
/**
* i40e_aq_get_rss_key
* @ hw : pointer to the hw struct
* @ vsi_id : vsi fw index
* @ key : pointer to key info struct
*
**/
int i40e_aq_get_rss_key(struct i40e_hw *hw,
u16 vsi_id,
struct i40e_aqc_get_set_rss_key_data *key)
{
return i40e_aq_get_set_rss_key(hw, vsi_id, key, false );
}
/**
* i40e_aq_set_rss_key
* @ hw : pointer to the hw struct
* @ vsi_id : vsi fw index
* @ key : pointer to key info struct
*
* set the RSS key per VSI
**/
int i40e_aq_set_rss_key(struct i40e_hw *hw,
u16 vsi_id,
struct i40e_aqc_get_set_rss_key_data *key)
{
return i40e_aq_get_set_rss_key(hw, vsi_id, key, true );
}
/**
* i40e_init_shared_code - Initialize the shared code
* @ hw : pointer to hardware structure
*
* This assigns the MAC type and PHY code and inits the NVM .
* Does not touch the hardware . This function must be called prior to any
* other function in the shared code . The i40e_hw structure should be
* memset to 0 prior to calling this function . The following fields in
* hw structure should be filled in prior to calling this function :
* hw_addr , back , device_id , vendor_id , subsystem_device_id ,
* subsystem_vendor_id , and revision_id
**/
int i40e_init_shared_code(struct i40e_hw *hw)
{
u32 port, ari, func_rid;
int status = 0 ;
i40e_set_mac_type(hw);
switch (hw->mac.type) {
case I40E_MAC_XL710:
case I40E_MAC_X722:
break ;
default :
return -ENODEV;
}
hw->phy.get_link_info = true ;
/* Determine port number and PF number*/
port = FIELD_GET(I40E_PFGEN_PORTNUM_PORT_NUM_MASK,
rd32(hw, I40E_PFGEN_PORTNUM));
hw->port = (u8)port;
ari = FIELD_GET(I40E_GLPCI_CAPSUP_ARI_EN_MASK,
rd32(hw, I40E_GLPCI_CAPSUP));
func_rid = rd32(hw, I40E_PF_FUNC_RID);
if (ari)
hw->pf_id = (u8)(func_rid & 0 xff);
else
hw->pf_id = (u8)(func_rid & 0 x7);
status = i40e_init_nvm(hw);
return status;
}
/**
* i40e_aq_mac_address_read - Retrieve the MAC addresses
* @ hw : pointer to the hw struct
* @ flags : a return indicator of what addresses were added to the addr store
* @ addrs : the requestor ' s mac addr store
* @ cmd_details : pointer to command details structure or NULL
**/
static int
i40e_aq_mac_address_read(struct i40e_hw *hw,
u16 *flags,
struct i40e_aqc_mac_address_read_data *addrs,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_mac_address_read *cmd_data;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_mac_address_read);
cmd_data = libie_aq_raw(&desc);
desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_BUF);
status = i40e_asq_send_command(hw, &desc, addrs,
sizeof (*addrs), cmd_details);
*flags = le16_to_cpu(cmd_data->command_flags);
return status;
}
/**
* i40e_aq_mac_address_write - Change the MAC addresses
* @ hw : pointer to the hw struct
* @ flags : indicates which MAC to be written
* @ mac_addr : address to write
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_mac_address_write(struct i40e_hw *hw,
u16 flags, u8 *mac_addr,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_mac_address_write *cmd_data;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_mac_address_write);
cmd_data = libie_aq_raw(&desc);
cmd_data->command_flags = cpu_to_le16(flags);
cmd_data->mac_sah = cpu_to_le16((u16)mac_addr[0 ] << 8 | mac_addr[1 ]);
cmd_data->mac_sal = cpu_to_le32(((u32)mac_addr[2 ] << 24 ) |
((u32)mac_addr[3 ] << 16 ) |
((u32)mac_addr[4 ] << 8 ) |
mac_addr[5 ]);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_get_mac_addr - get MAC address
* @ hw : pointer to the HW structure
* @ mac_addr : pointer to MAC address
*
* Reads the adapter ' s MAC address from register
**/
int i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
{
struct i40e_aqc_mac_address_read_data addrs;
u16 flags = 0 ;
int status;
status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL);
if (flags & I40E_AQC_LAN_ADDR_VALID)
ether_addr_copy(mac_addr, addrs.pf_lan_mac);
return status;
}
/**
* i40e_get_port_mac_addr - get Port MAC address
* @ hw : pointer to the HW structure
* @ mac_addr : pointer to Port MAC address
*
* Reads the adapter ' s Port MAC address
**/
int i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
{
struct i40e_aqc_mac_address_read_data addrs;
u16 flags = 0 ;
int status;
status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL);
if (status)
return status;
if (flags & I40E_AQC_PORT_ADDR_VALID)
ether_addr_copy(mac_addr, addrs.port_mac);
else
status = -EINVAL;
return status;
}
/**
* i40e_pre_tx_queue_cfg - pre tx queue configure
* @ hw : pointer to the HW structure
* @ queue : target PF queue index
* @ enable : state change request
*
* Handles hw requirement to indicate intention to enable
* or disable target queue .
**/
void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable)
{
u32 abs_queue_idx = hw->func_caps.base_queue + queue;
u32 reg_block = 0 ;
u32 reg_val;
if (abs_queue_idx >= 128 ) {
reg_block = abs_queue_idx / 128 ;
abs_queue_idx %= 128 ;
}
reg_val = rd32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block));
reg_val &= ~I40E_GLLAN_TXPRE_QDIS_QINDX_MASK;
reg_val |= (abs_queue_idx << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT);
if (enable)
reg_val |= I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK;
else
reg_val |= I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK;
wr32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block), reg_val);
}
/**
* i40e_get_pba_string - Reads part number string from EEPROM
* @ hw : pointer to hardware structure
*
* Reads the part number string from the EEPROM and stores it
* into newly allocated buffer and saves resulting pointer
* to i40e_hw - > pba_id field .
**/
void i40e_get_pba_string(struct i40e_hw *hw)
{
#define I40E_NVM_PBA_FLAGS_BLK_PRESENT 0 xFAFA
u16 pba_word = 0 ;
u16 pba_size = 0 ;
u16 pba_ptr = 0 ;
int status;
char *ptr;
u16 i;
status = i40e_read_nvm_word(hw, I40E_SR_PBA_FLAGS, &pba_word);
if (status) {
hw_dbg(hw, "Failed to read PBA flags.\n" );
return ;
}
if (pba_word != I40E_NVM_PBA_FLAGS_BLK_PRESENT) {
hw_dbg(hw, "PBA block is not present.\n" );
return ;
}
status = i40e_read_nvm_word(hw, I40E_SR_PBA_BLOCK_PTR, &pba_ptr);
if (status) {
hw_dbg(hw, "Failed to read PBA Block pointer.\n" );
return ;
}
status = i40e_read_nvm_word(hw, pba_ptr, &pba_size);
if (status) {
hw_dbg(hw, "Failed to read PBA Block size.\n" );
return ;
}
/* Subtract one to get PBA word count (PBA Size word is included in
* total size ) and advance pointer to first PBA word .
*/
pba_size--;
pba_ptr++;
if (!pba_size) {
hw_dbg(hw, "PBA ID is empty.\n" );
return ;
}
ptr = devm_kzalloc(i40e_hw_to_dev(hw), pba_size * 2 + 1 , GFP_KERNEL);
if (!ptr)
return ;
hw->pba_id = ptr;
for (i = 0 ; i < pba_size; i++) {
status = i40e_read_nvm_word(hw, pba_ptr + i, &pba_word);
if (status) {
hw_dbg(hw, "Failed to read PBA Block word %d.\n" , i);
devm_kfree(i40e_hw_to_dev(hw), hw->pba_id);
hw->pba_id = NULL;
return ;
}
*ptr++ = (pba_word >> 8 ) & 0 xFF;
*ptr++ = pba_word & 0 xFF;
}
}
/**
* i40e_get_media_type - Gets media type
* @ hw : pointer to the hardware structure
**/
static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw)
{
enum i40e_media_type media;
switch (hw->phy.link_info.phy_type) {
case I40E_PHY_TYPE_10GBASE_SR:
case I40E_PHY_TYPE_10GBASE_LR:
case I40E_PHY_TYPE_1000BASE_SX:
case I40E_PHY_TYPE_1000BASE_LX:
case I40E_PHY_TYPE_40GBASE_SR4:
case I40E_PHY_TYPE_40GBASE_LR4:
case I40E_PHY_TYPE_25GBASE_LR:
case I40E_PHY_TYPE_25GBASE_SR:
media = I40E_MEDIA_TYPE_FIBER;
break ;
case I40E_PHY_TYPE_100BASE_TX:
case I40E_PHY_TYPE_1000BASE_T:
case I40E_PHY_TYPE_2_5GBASE_T_LINK_STATUS:
case I40E_PHY_TYPE_5GBASE_T_LINK_STATUS:
case I40E_PHY_TYPE_10GBASE_T:
media = I40E_MEDIA_TYPE_BASET;
break ;
case I40E_PHY_TYPE_10GBASE_CR1_CU:
case I40E_PHY_TYPE_40GBASE_CR4_CU:
case I40E_PHY_TYPE_10GBASE_CR1:
case I40E_PHY_TYPE_40GBASE_CR4:
case I40E_PHY_TYPE_10GBASE_SFPP_CU:
case I40E_PHY_TYPE_40GBASE_AOC:
case I40E_PHY_TYPE_10GBASE_AOC:
case I40E_PHY_TYPE_25GBASE_CR:
case I40E_PHY_TYPE_25GBASE_AOC:
case I40E_PHY_TYPE_25GBASE_ACC:
media = I40E_MEDIA_TYPE_DA;
break ;
case I40E_PHY_TYPE_1000BASE_KX:
case I40E_PHY_TYPE_10GBASE_KX4:
case I40E_PHY_TYPE_10GBASE_KR:
case I40E_PHY_TYPE_40GBASE_KR4:
case I40E_PHY_TYPE_20GBASE_KR2:
case I40E_PHY_TYPE_25GBASE_KR:
media = I40E_MEDIA_TYPE_BACKPLANE;
break ;
case I40E_PHY_TYPE_SGMII:
case I40E_PHY_TYPE_XAUI:
case I40E_PHY_TYPE_XFI:
case I40E_PHY_TYPE_XLAUI:
case I40E_PHY_TYPE_XLPPI:
default :
media = I40E_MEDIA_TYPE_UNKNOWN;
break ;
}
return media;
}
/**
* i40e_poll_globr - Poll for Global Reset completion
* @ hw : pointer to the hardware structure
* @ retry_limit : how many times to retry before failure
**/
static int i40e_poll_globr(struct i40e_hw *hw,
u32 retry_limit)
{
u32 cnt, reg = 0 ;
for (cnt = 0 ; cnt < retry_limit; cnt++) {
reg = rd32(hw, I40E_GLGEN_RSTAT);
if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK))
return 0 ;
msleep(100 );
}
hw_dbg(hw, "Global reset failed.\n" );
hw_dbg(hw, "I40E_GLGEN_RSTAT = 0x%x\n" , reg);
return -EIO;
}
#define I40E_PF_RESET_WAIT_COUNT_A0 200
#define I40E_PF_RESET_WAIT_COUNT 200
/**
* i40e_pf_reset - Reset the PF
* @ hw : pointer to the hardware structure
*
* Assuming someone else has triggered a global reset ,
* assure the global reset is complete and then reset the PF
**/
int i40e_pf_reset(struct i40e_hw *hw)
{
u32 cnt = 0 ;
u32 cnt1 = 0 ;
u32 reg = 0 ;
u32 grst_del;
/* Poll for Global Reset steady state in case of recent GRST.
* The grst delay value is in 100 ms units , and we ' ll wait a
* couple counts longer to be sure we don ' t just miss the end .
*/
grst_del = FIELD_GET(I40E_GLGEN_RSTCTL_GRSTDEL_MASK,
rd32(hw, I40E_GLGEN_RSTCTL));
/* It can take upto 15 secs for GRST steady state.
* Bump it to 16 secs max to be safe .
*/
grst_del = grst_del * 20 ;
for (cnt = 0 ; cnt < grst_del; cnt++) {
reg = rd32(hw, I40E_GLGEN_RSTAT);
if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK))
break ;
msleep(100 );
}
if (reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK) {
hw_dbg(hw, "Global reset polling failed to complete.\n" );
return -EIO;
}
/* Now Wait for the FW to be ready */
for (cnt1 = 0 ; cnt1 < I40E_PF_RESET_WAIT_COUNT; cnt1++) {
reg = rd32(hw, I40E_GLNVM_ULD);
reg &= (I40E_GLNVM_ULD_CONF_CORE_DONE_MASK |
I40E_GLNVM_ULD_CONF_GLOBAL_DONE_MASK);
if (reg == (I40E_GLNVM_ULD_CONF_CORE_DONE_MASK |
I40E_GLNVM_ULD_CONF_GLOBAL_DONE_MASK)) {
hw_dbg(hw, "Core and Global modules ready %d\n" , cnt1);
break ;
}
usleep_range(10000 , 20000 );
}
if (!(reg & (I40E_GLNVM_ULD_CONF_CORE_DONE_MASK |
I40E_GLNVM_ULD_CONF_GLOBAL_DONE_MASK))) {
hw_dbg(hw, "wait for FW Reset complete timedout\n" );
hw_dbg(hw, "I40E_GLNVM_ULD = 0x%x\n" , reg);
return -EIO;
}
/* If there was a Global Reset in progress when we got here,
* we don ' t need to do the PF Reset
*/
if (!cnt) {
u32 reg2 = 0 ;
if (hw->revision_id == 0 )
cnt = I40E_PF_RESET_WAIT_COUNT_A0;
else
cnt = I40E_PF_RESET_WAIT_COUNT;
reg = rd32(hw, I40E_PFGEN_CTRL);
wr32(hw, I40E_PFGEN_CTRL,
(reg | I40E_PFGEN_CTRL_PFSWR_MASK));
for (; cnt; cnt--) {
reg = rd32(hw, I40E_PFGEN_CTRL);
if (!(reg & I40E_PFGEN_CTRL_PFSWR_MASK))
break ;
reg2 = rd32(hw, I40E_GLGEN_RSTAT);
if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK)
break ;
usleep_range(1000 , 2000 );
}
if (reg2 & I40E_GLGEN_RSTAT_DEVSTATE_MASK) {
if (i40e_poll_globr(hw, grst_del))
return -EIO;
} else if (reg & I40E_PFGEN_CTRL_PFSWR_MASK) {
hw_dbg(hw, "PF reset polling failed to complete.\n" );
return -EIO;
}
}
i40e_clear_pxe_mode(hw);
return 0 ;
}
/**
* i40e_clear_hw - clear out any left over hw state
* @ hw : pointer to the hw struct
*
* Clear queues and interrupts , typically called at init time ,
* but after the capabilities have been found so we know how many
* queues and msix vectors have been allocated .
**/
void i40e_clear_hw(struct i40e_hw *hw)
{
u32 num_queues, base_queue;
s32 num_pf_int;
s32 num_vf_int;
u32 num_vfs;
s32 i;
u32 j;
u32 val;
u32 eol = 0 x7ff;
/* get number of interrupts, queues, and VFs */
val = rd32(hw, I40E_GLPCI_CNF2);
num_pf_int = FIELD_GET(I40E_GLPCI_CNF2_MSI_X_PF_N_MASK, val);
num_vf_int = FIELD_GET(I40E_GLPCI_CNF2_MSI_X_VF_N_MASK, val);
val = rd32(hw, I40E_PFLAN_QALLOC);
base_queue = FIELD_GET(I40E_PFLAN_QALLOC_FIRSTQ_MASK, val);
j = FIELD_GET(I40E_PFLAN_QALLOC_LASTQ_MASK, val);
if (val & I40E_PFLAN_QALLOC_VALID_MASK && j >= base_queue)
num_queues = (j - base_queue) + 1 ;
else
num_queues = 0 ;
val = rd32(hw, I40E_PF_VT_PFALLOC);
i = FIELD_GET(I40E_PF_VT_PFALLOC_FIRSTVF_MASK, val);
j = FIELD_GET(I40E_PF_VT_PFALLOC_LASTVF_MASK, val);
if (val & I40E_PF_VT_PFALLOC_VALID_MASK && j >= i)
num_vfs = (j - i) + 1 ;
else
num_vfs = 0 ;
/* stop all the interrupts */
wr32(hw, I40E_PFINT_ICR0_ENA, 0 );
val = 0 x3 << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT;
for (i = 0 ; i < num_pf_int - 2 ; i++)
wr32(hw, I40E_PFINT_DYN_CTLN(i), val);
/* Set the FIRSTQ_INDX field to 0x7FF in PFINT_LNKLSTx */
val = eol << I40E_PFINT_LNKLST0_FIRSTQ_INDX_SHIFT;
wr32(hw, I40E_PFINT_LNKLST0, val);
for (i = 0 ; i < num_pf_int - 2 ; i++)
wr32(hw, I40E_PFINT_LNKLSTN(i), val);
val = eol << I40E_VPINT_LNKLST0_FIRSTQ_INDX_SHIFT;
for (i = 0 ; i < num_vfs; i++)
wr32(hw, I40E_VPINT_LNKLST0(i), val);
for (i = 0 ; i < num_vf_int - 2 ; i++)
wr32(hw, I40E_VPINT_LNKLSTN(i), val);
/* warn the HW of the coming Tx disables */
for (i = 0 ; i < num_queues; i++) {
u32 abs_queue_idx = base_queue + i;
u32 reg_block = 0 ;
if (abs_queue_idx >= 128 ) {
reg_block = abs_queue_idx / 128 ;
abs_queue_idx %= 128 ;
}
val = rd32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block));
val &= ~I40E_GLLAN_TXPRE_QDIS_QINDX_MASK;
val |= (abs_queue_idx << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT);
val |= I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK;
wr32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block), val);
}
udelay(400 );
/* stop all the queues */
for (i = 0 ; i < num_queues; i++) {
wr32(hw, I40E_QINT_TQCTL(i), 0 );
wr32(hw, I40E_QTX_ENA(i), 0 );
wr32(hw, I40E_QINT_RQCTL(i), 0 );
wr32(hw, I40E_QRX_ENA(i), 0 );
}
/* short wait for all queue disables to settle */
udelay(50 );
}
/**
* i40e_clear_pxe_mode - clear pxe operations mode
* @ hw : pointer to the hw struct
*
* Make sure all PXE mode settings are cleared , including things
* like descriptor fetch / write - back mode .
**/
void i40e_clear_pxe_mode(struct i40e_hw *hw)
{
u32 reg;
if (i40e_check_asq_alive(hw))
i40e_aq_clear_pxe_mode(hw, NULL);
/* Clear single descriptor fetch/write-back mode */
reg = rd32(hw, I40E_GLLAN_RCTL_0);
if (hw->revision_id == 0 ) {
/* As a work around clear PXE_MODE instead of setting it */
wr32(hw, I40E_GLLAN_RCTL_0, (reg & (~I40E_GLLAN_RCTL_0_PXE_MODE_MASK)));
} else {
wr32(hw, I40E_GLLAN_RCTL_0, (reg | I40E_GLLAN_RCTL_0_PXE_MODE_MASK));
}
}
/**
* i40e_led_is_mine - helper to find matching led
* @ hw : pointer to the hw struct
* @ idx : index into GPIO registers
*
* returns : 0 if no match , otherwise the value of the GPIO_CTL register
*/
static u32 i40e_led_is_mine(struct i40e_hw *hw, int idx)
{
u32 gpio_val = 0 ;
u32 port;
if (!I40E_IS_X710TL_DEVICE(hw->device_id) &&
!hw->func_caps.led[idx])
return 0 ;
gpio_val = rd32(hw, I40E_GLGEN_GPIO_CTL(idx));
port = FIELD_GET(I40E_GLGEN_GPIO_CTL_PRT_NUM_MASK, gpio_val);
/* if PRT_NUM_NA is 1 then this LED is not port specific, OR
* if it is not our port then ignore
*/
if ((gpio_val & I40E_GLGEN_GPIO_CTL_PRT_NUM_NA_MASK) ||
(port != hw->port))
return 0 ;
return gpio_val;
}
#define I40E_FW_LED BIT(4 )
#define I40E_LED_MODE_VALID (I40E_GLGEN_GPIO_CTL_LED_MODE_MASK >> \
I40E_GLGEN_GPIO_CTL_LED_MODE_SHIFT)
#define I40E_LED0 22
#define I40E_PIN_FUNC_SDP 0 x0
#define I40E_PIN_FUNC_LED 0 x1
/**
* i40e_led_get - return current on / off mode
* @ hw : pointer to the hw struct
*
* The value returned is the ' mode ' field as defined in the
* GPIO register definitions : 0 x0 = off , 0 xf = on , and other
* values are variations of possible behaviors relating to
* blink , link , and wire .
**/
u32 i40e_led_get(struct i40e_hw *hw)
{
u32 mode = 0 ;
int i;
/* as per the documentation GPIO 22-29 are the LED
* GPIO pins named LED0 . . LED7
*/
for (i = I40E_LED0; i <= I40E_GLGEN_GPIO_CTL_MAX_INDEX; i++) {
u32 gpio_val = i40e_led_is_mine(hw, i);
if (!gpio_val)
continue ;
mode = FIELD_GET(I40E_GLGEN_GPIO_CTL_LED_MODE_MASK, gpio_val);
break ;
}
return mode;
}
/**
* i40e_led_set - set new on / off mode
* @ hw : pointer to the hw struct
* @ mode : 0 = off , 0 xf = on ( else see manual for mode details )
* @ blink : true if the LED should blink when on , false if steady
*
* if this function is used to turn on the blink it should
* be used to disable the blink when restoring the original state .
**/
void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink)
{
int i;
if (mode & ~I40E_LED_MODE_VALID) {
hw_dbg(hw, "invalid mode passed in %X\n" , mode);
return ;
}
/* as per the documentation GPIO 22-29 are the LED
* GPIO pins named LED0 . . LED7
*/
for (i = I40E_LED0; i <= I40E_GLGEN_GPIO_CTL_MAX_INDEX; i++) {
u32 gpio_val = i40e_led_is_mine(hw, i);
if (!gpio_val)
continue ;
if (I40E_IS_X710TL_DEVICE(hw->device_id)) {
u32 pin_func = 0 ;
if (mode & I40E_FW_LED)
pin_func = I40E_PIN_FUNC_SDP;
else
pin_func = I40E_PIN_FUNC_LED;
gpio_val &= ~I40E_GLGEN_GPIO_CTL_PIN_FUNC_MASK;
gpio_val |=
FIELD_PREP(I40E_GLGEN_GPIO_CTL_PIN_FUNC_MASK,
pin_func);
}
gpio_val &= ~I40E_GLGEN_GPIO_CTL_LED_MODE_MASK;
/* this & is a bit of paranoia, but serves as a range check */
gpio_val |= FIELD_PREP(I40E_GLGEN_GPIO_CTL_LED_MODE_MASK,
mode);
if (blink)
gpio_val |= BIT(I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT);
else
gpio_val &= ~BIT(I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT);
wr32(hw, I40E_GLGEN_GPIO_CTL(i), gpio_val);
break ;
}
}
/* Admin command wrappers */
/**
* i40e_aq_get_phy_capabilities
* @ hw : pointer to the hw struct
* @ abilities : structure for PHY capabilities to be filled
* @ qualified_modules : report Qualified Modules
* @ report_init : report init capabilities ( active are default )
* @ cmd_details : pointer to command details structure or NULL
*
* Returns the various PHY abilities supported on the Port .
**/
int
i40e_aq_get_phy_capabilities(struct i40e_hw *hw,
bool qualified_modules, bool report_init,
struct i40e_aq_get_phy_abilities_resp *abilities,
struct i40e_asq_cmd_details *cmd_details)
{
u16 abilities_size = sizeof (struct i40e_aq_get_phy_abilities_resp);
u16 max_delay = I40E_MAX_PHY_TIMEOUT, total_delay = 0 ;
struct libie_aq_desc desc;
int status;
if (!abilities)
return -EINVAL;
do {
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_phy_abilities);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (abilities_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
if (qualified_modules)
desc.params.generic.param0 |=
cpu_to_le32(I40E_AQ_PHY_REPORT_QUALIFIED_MODULES);
if (report_init)
desc.params.generic.param0 |=
cpu_to_le32(I40E_AQ_PHY_REPORT_INITIAL_VALUES);
status = i40e_asq_send_command(hw, &desc, abilities,
abilities_size, cmd_details);
switch (hw->aq.asq_last_status) {
case LIBIE_AQ_RC_EIO:
status = -EIO;
break ;
case LIBIE_AQ_RC_EAGAIN:
usleep_range(1000 , 2000 );
total_delay++;
status = -EIO;
break ;
/* also covers LIBIE_AQ_RC_OK */
default :
break ;
}
} while ((hw->aq.asq_last_status == LIBIE_AQ_RC_EAGAIN) &&
(total_delay < max_delay));
if (status)
return status;
if (report_init) {
if (hw->mac.type == I40E_MAC_XL710 &&
i40e_is_aq_api_ver_ge(hw, I40E_FW_API_VERSION_MAJOR,
I40E_MINOR_VER_GET_LINK_INFO_XL710)) {
status = i40e_aq_get_link_info(hw, true , NULL, NULL);
} else {
hw->phy.phy_types = le32_to_cpu(abilities->phy_type);
hw->phy.phy_types |=
((u64)abilities->phy_type_ext << 32 );
}
}
return status;
}
/**
* i40e_aq_set_phy_config
* @ hw : pointer to the hw struct
* @ config : structure with PHY configuration to be set
* @ cmd_details : pointer to command details structure or NULL
*
* Set the various PHY configuration parameters
* supported on the Port . One or more of the Set PHY config parameters may be
* ignored in an MFP mode as the PF may not have the privilege to set some
* of the PHY Config parameters . This status will be indicated by the
* command response .
**/
int i40e_aq_set_phy_config(struct i40e_hw *hw,
struct i40e_aq_set_phy_config *config,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aq_set_phy_config *cmd;
struct libie_aq_desc desc;
int status;
if (!config)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_phy_config);
cmd = libie_aq_raw(&desc);
*cmd = *config;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
static noinline_for_stack int
i40e_set_fc_status(struct i40e_hw *hw,
struct i40e_aq_get_phy_abilities_resp *abilities,
bool atomic_restart)
{
struct i40e_aq_set_phy_config config;
enum i40e_fc_mode fc_mode = hw->fc.requested_mode;
u8 pause_mask = 0 x0;
switch (fc_mode) {
case I40E_FC_FULL:
pause_mask |= I40E_AQ_PHY_FLAG_PAUSE_TX;
pause_mask |= I40E_AQ_PHY_FLAG_PAUSE_RX;
break ;
case I40E_FC_RX_PAUSE:
pause_mask |= I40E_AQ_PHY_FLAG_PAUSE_RX;
break ;
case I40E_FC_TX_PAUSE:
pause_mask |= I40E_AQ_PHY_FLAG_PAUSE_TX;
break ;
default :
break ;
}
memset(&config, 0 , sizeof (struct i40e_aq_set_phy_config));
/* clear the old pause settings */
config.abilities = abilities->abilities & ~(I40E_AQ_PHY_FLAG_PAUSE_TX) &
~(I40E_AQ_PHY_FLAG_PAUSE_RX);
/* set the new abilities */
config.abilities |= pause_mask;
/* If the abilities have changed, then set the new config */
if (config.abilities == abilities->abilities)
return 0 ;
/* Auto restart link so settings take effect */
if (atomic_restart)
config.abilities |= I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
/* Copy over all the old settings */
config.phy_type = abilities->phy_type;
config.phy_type_ext = abilities->phy_type_ext;
config.link_speed = abilities->link_speed;
config.eee_capability = abilities->eee_capability;
config.eeer = abilities->eeer_val;
config.low_power_ctrl = abilities->d3_lpan;
config.fec_config = abilities->fec_cfg_curr_mod_ext_info &
I40E_AQ_PHY_FEC_CONFIG_MASK;
return i40e_aq_set_phy_config(hw, &config, NULL);
}
/**
* i40e_set_fc
* @ hw : pointer to the hw struct
* @ aq_failures : buffer to return AdminQ failure information
* @ atomic_restart : whether to enable atomic link restart
*
* Set the requested flow control mode using set_phy_config .
**/
int i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures,
bool atomic_restart)
{
struct i40e_aq_get_phy_abilities_resp abilities;
int status;
*aq_failures = 0 x0;
/* Get the current phy config */
status = i40e_aq_get_phy_capabilities(hw, false , false , &abilities,
NULL);
if (status) {
*aq_failures |= I40E_SET_FC_AQ_FAIL_GET;
return status;
}
status = i40e_set_fc_status(hw, &abilities, atomic_restart);
if (status)
*aq_failures |= I40E_SET_FC_AQ_FAIL_SET;
/* Update the link info */
status = i40e_update_link_info(hw);
if (status) {
/* Wait a little bit (on 40G cards it sometimes takes a really
* long time for link to come back from the atomic reset )
* and try once more
*/
msleep(1000 );
status = i40e_update_link_info(hw);
}
if (status)
*aq_failures |= I40E_SET_FC_AQ_FAIL_UPDATE;
return status;
}
/**
* i40e_aq_set_mac_config - Configure MAC settings
* @ hw : pointer to the hw struct
* @ max_frame_size : Maximum Frame Size to be supported by the port
* @ cmd_details : pointer to command details structure or NULL
*
* Set MAC configuration ( 0 x0603 ) . Note that max_frame_size must be greater
* than zero .
*
* Return : 0 on success , or a negative error code on failure .
*/
int i40e_aq_set_mac_config(struct i40e_hw *hw, u16 max_frame_size,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aq_set_mac_config *cmd;
struct libie_aq_desc desc;
cmd = libie_aq_raw(&desc);
if (max_frame_size == 0 )
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_mac_config);
cmd->max_frame_size = cpu_to_le16(max_frame_size);
cmd->params = I40E_AQ_SET_MAC_CONFIG_CRC_EN;
#define I40E_AQ_SET_MAC_CONFIG_FC_DEFAULT_THRESHOLD 0 x7FFF
cmd->fc_refresh_threshold =
cpu_to_le16(I40E_AQ_SET_MAC_CONFIG_FC_DEFAULT_THRESHOLD);
return i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
}
/**
* i40e_aq_clear_pxe_mode
* @ hw : pointer to the hw struct
* @ cmd_details : pointer to command details structure or NULL
*
* Tell the firmware that the driver is taking over from PXE
**/
int i40e_aq_clear_pxe_mode(struct i40e_hw *hw,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_clear_pxe *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_clear_pxe_mode);
cmd = libie_aq_raw(&desc);
cmd->rx_cnt = 0 x2;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
wr32(hw, I40E_GLLAN_RCTL_0, 0 x1);
return status;
}
/**
* i40e_aq_set_link_restart_an
* @ hw : pointer to the hw struct
* @ enable_link : if true : enable link , if false : disable link
* @ cmd_details : pointer to command details structure or NULL
*
* Sets up the link and restarts the Auto - Negotiation over the link .
**/
int i40e_aq_set_link_restart_an(struct i40e_hw *hw,
bool enable_link,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_link_restart_an *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_link_restart_an);
cmd = libie_aq_raw(&desc);
cmd->command = I40E_AQ_PHY_RESTART_AN;
if (enable_link)
cmd->command |= I40E_AQ_PHY_LINK_ENABLE;
else
cmd->command &= ~I40E_AQ_PHY_LINK_ENABLE;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_get_link_info
* @ hw : pointer to the hw struct
* @ enable_lse : enable / disable LinkStatusEvent reporting
* @ link : pointer to link status structure - optional
* @ cmd_details : pointer to command details structure or NULL
*
* Returns the link status of the adapter .
**/
int i40e_aq_get_link_info(struct i40e_hw *hw,
bool enable_lse, struct i40e_link_status *link,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_link_status *hw_link_info = &hw->phy.link_info;
struct i40e_aqc_get_link_status *resp;
struct libie_aq_desc desc;
bool tx_pause, rx_pause;
u16 command_flags;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_link_status);
resp = libie_aq_raw(&desc);
if (enable_lse)
command_flags = I40E_AQ_LSE_ENABLE;
else
command_flags = I40E_AQ_LSE_DISABLE;
resp->command_flags = cpu_to_le16(command_flags);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (status)
goto aq_get_link_info_exit;
/* save off old link status information */
hw->phy.link_info_old = *hw_link_info;
/* update link status */
hw_link_info->phy_type = (enum i40e_aq_phy_type)resp->phy_type;
hw->phy.media_type = i40e_get_media_type(hw);
hw_link_info->link_speed = (enum i40e_aq_link_speed)resp->link_speed;
hw_link_info->link_info = resp->link_info;
hw_link_info->an_info = resp->an_info;
hw_link_info->fec_info = resp->config & (I40E_AQ_CONFIG_FEC_KR_ENA |
I40E_AQ_CONFIG_FEC_RS_ENA);
hw_link_info->ext_info = resp->ext_info;
hw_link_info->loopback = resp->loopback & I40E_AQ_LOOPBACK_MASK;
hw_link_info->max_frame_size = le16_to_cpu(resp->max_frame_size);
hw_link_info->pacing = resp->config & I40E_AQ_CONFIG_PACING_MASK;
/* update fc info */
tx_pause = !!(resp->an_info & I40E_AQ_LINK_PAUSE_TX);
rx_pause = !!(resp->an_info & I40E_AQ_LINK_PAUSE_RX);
if (tx_pause & rx_pause)
hw->fc.current_mode = I40E_FC_FULL;
else if (tx_pause)
hw->fc.current_mode = I40E_FC_TX_PAUSE;
else if (rx_pause)
hw->fc.current_mode = I40E_FC_RX_PAUSE;
else
hw->fc.current_mode = I40E_FC_NONE;
if (resp->config & I40E_AQ_CONFIG_CRC_ENA)
hw_link_info->crc_enable = true ;
else
hw_link_info->crc_enable = false ;
if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_IS_ENABLED))
hw_link_info->lse_enable = true ;
else
hw_link_info->lse_enable = false ;
if (hw->mac.type == I40E_MAC_XL710 && i40e_is_fw_ver_lt(hw, 4 , 40 ) &&
hw_link_info->phy_type == 0 xE)
hw_link_info->phy_type = I40E_PHY_TYPE_10GBASE_SFPP_CU;
if (test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps) &&
hw->mac.type != I40E_MAC_X722) {
__le32 tmp;
memcpy(&tmp, resp->link_type, sizeof (tmp));
hw->phy.phy_types = le32_to_cpu(tmp);
hw->phy.phy_types |= ((u64)resp->link_type_ext << 32 );
}
/* save link status information */
if (link)
*link = *hw_link_info;
/* flag cleared so helper functions don't call AQ again */
hw->phy.get_link_info = false ;
aq_get_link_info_exit:
return status;
}
/**
* i40e_aq_set_phy_int_mask
* @ hw : pointer to the hw struct
* @ mask : interrupt mask to be set
* @ cmd_details : pointer to command details structure or NULL
*
* Set link interrupt mask .
**/
int i40e_aq_set_phy_int_mask(struct i40e_hw *hw,
u16 mask,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_phy_int_mask *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_phy_int_mask);
cmd = libie_aq_raw(&desc);
cmd->event_mask = cpu_to_le16(mask);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_mac_loopback
* @ hw : pointer to the HW struct
* @ ena_lpbk : Enable or Disable loopback
* @ cmd_details : pointer to command details structure or NULL
*
* Enable / disable loopback on a given port
*/
int i40e_aq_set_mac_loopback(struct i40e_hw *hw, bool ena_lpbk,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_lb_mode *cmd;
struct libie_aq_desc desc;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_set_lb_modes);
cmd = libie_aq_raw(&desc);
if (ena_lpbk) {
if (hw->nvm.version <= I40E_LEGACY_LOOPBACK_NVM_VER)
cmd->lb_mode = cpu_to_le16(I40E_AQ_LB_MAC_LOCAL_LEGACY);
else
cmd->lb_mode = cpu_to_le16(I40E_AQ_LB_MAC_LOCAL);
}
return i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
}
/**
* i40e_aq_set_phy_debug
* @ hw : pointer to the hw struct
* @ cmd_flags : debug command flags
* @ cmd_details : pointer to command details structure or NULL
*
* Reset the external PHY .
**/
int i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_phy_debug *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_phy_debug);
cmd = libie_aq_raw(&desc);
cmd->command_flags = cmd_flags;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_add_vsi
* @ hw : pointer to the hw struct
* @ vsi_ctx : pointer to a vsi context struct
* @ cmd_details : pointer to command details structure or NULL
*
* Add a VSI context to the hardware .
**/
int i40e_aq_add_vsi(struct i40e_hw *hw,
struct i40e_vsi_context *vsi_ctx,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_add_get_update_vsi_completion *resp;
struct i40e_aqc_add_get_update_vsi *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_add_vsi);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
cmd->uplink_seid = cpu_to_le16(vsi_ctx->uplink_seid);
cmd->connection_type = vsi_ctx->connection_type;
cmd->vf_id = vsi_ctx->vf_num;
cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info,
sizeof (vsi_ctx->info),
cmd_details, true );
if (status)
goto aq_add_vsi_exit;
vsi_ctx->seid = le16_to_cpu(resp->seid);
vsi_ctx->vsi_number = le16_to_cpu(resp->vsi_number);
vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
aq_add_vsi_exit:
return status;
}
/**
* i40e_aq_set_default_vsi
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_set_default_vsi(struct i40e_hw *hw,
u16 seid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
cmd->promiscuous_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_clear_default_vsi
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_clear_default_vsi(struct i40e_hw *hw,
u16 seid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
cmd->promiscuous_flags = cpu_to_le16(0 );
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_DEFAULT);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_vsi_unicast_promiscuous
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ set : set unicast promiscuous enable / disable
* @ cmd_details : pointer to command details structure or NULL
* @ rx_only_promisc : flag to decide if egress traffic gets mirrored in promisc
**/
int i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw,
u16 seid, bool set,
struct i40e_asq_cmd_details *cmd_details,
bool rx_only_promisc)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
u16 flags = 0 ;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
if (set) {
flags |= I40E_AQC_SET_VSI_PROMISC_UNICAST;
if (rx_only_promisc && i40e_is_aq_api_ver_ge(hw, 1 , 5 ))
flags |= I40E_AQC_SET_VSI_PROMISC_RX_ONLY;
}
cmd->promiscuous_flags = cpu_to_le16(flags);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_UNICAST);
if (i40e_is_aq_api_ver_ge(hw, 1 , 5 ))
cmd->valid_flags |=
cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_RX_ONLY);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_vsi_multicast_promiscuous
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ set : set multicast promiscuous enable / disable
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_set_vsi_multicast_promiscuous(struct i40e_hw *hw,
u16 seid, bool set,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
u16 flags = 0 ;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
if (set)
flags |= I40E_AQC_SET_VSI_PROMISC_MULTICAST;
cmd->promiscuous_flags = cpu_to_le16(flags);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_MULTICAST);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_vsi_mc_promisc_on_vlan
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ enable : set MAC L2 layer unicast promiscuous enable / disable for a given VLAN
* @ vid : The VLAN tag filter - capture any multicast packet with this VLAN tag
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw,
u16 seid, bool enable,
u16 vid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
u16 flags = 0 ;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
if (enable)
flags |= I40E_AQC_SET_VSI_PROMISC_MULTICAST;
cmd->promiscuous_flags = cpu_to_le16(flags);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_MULTICAST);
cmd->seid = cpu_to_le16(seid);
cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID);
status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0 ,
cmd_details, true );
return status;
}
/**
* i40e_aq_set_vsi_uc_promisc_on_vlan
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ enable : set MAC L2 layer unicast promiscuous enable / disable for a given VLAN
* @ vid : The VLAN tag filter - capture any unicast packet with this VLAN tag
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_set_vsi_uc_promisc_on_vlan(struct i40e_hw *hw,
u16 seid, bool enable,
u16 vid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
u16 flags = 0 ;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
if (enable) {
flags |= I40E_AQC_SET_VSI_PROMISC_UNICAST;
if (i40e_is_aq_api_ver_ge(hw, 1 , 5 ))
flags |= I40E_AQC_SET_VSI_PROMISC_RX_ONLY;
}
cmd->promiscuous_flags = cpu_to_le16(flags);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_UNICAST);
if (i40e_is_aq_api_ver_ge(hw, 1 , 5 ))
cmd->valid_flags |=
cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_RX_ONLY);
cmd->seid = cpu_to_le16(seid);
cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID);
status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0 ,
cmd_details, true );
return status;
}
/**
* i40e_aq_set_vsi_bc_promisc_on_vlan
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ enable : set broadcast promiscuous enable / disable for a given VLAN
* @ vid : The VLAN tag filter - capture any broadcast packet with this VLAN tag
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_set_vsi_bc_promisc_on_vlan(struct i40e_hw *hw,
u16 seid, bool enable, u16 vid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
u16 flags = 0 ;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
if (enable)
flags |= I40E_AQC_SET_VSI_PROMISC_BROADCAST;
cmd = libie_aq_raw(&desc);
cmd->promiscuous_flags = cpu_to_le16(flags);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST);
cmd->seid = cpu_to_le16(seid);
cmd->vlan_tag = cpu_to_le16(vid | I40E_AQC_SET_VSI_VLAN_VALID);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_vsi_broadcast
* @ hw : pointer to the hw struct
* @ seid : vsi number
* @ set_filter : true to set filter , false to clear filter
* @ cmd_details : pointer to command details structure or NULL
*
* Set or clear the broadcast promiscuous flag ( filter ) for a given VSI .
**/
int i40e_aq_set_vsi_broadcast(struct i40e_hw *hw,
u16 seid, bool set_filter,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_vsi_promiscuous_modes *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_vsi_promiscuous_modes);
cmd = libie_aq_raw(&desc);
if (set_filter)
cmd->promiscuous_flags
|= cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST);
else
cmd->promiscuous_flags
&= cpu_to_le16(~I40E_AQC_SET_VSI_PROMISC_BROADCAST);
cmd->valid_flags = cpu_to_le16(I40E_AQC_SET_VSI_PROMISC_BROADCAST);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_get_vsi_params - get VSI configuration info
* @ hw : pointer to the hw struct
* @ vsi_ctx : pointer to a vsi context struct
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_get_vsi_params(struct i40e_hw *hw,
struct i40e_vsi_context *vsi_ctx,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_add_get_update_vsi_completion *resp;
struct i40e_aqc_add_get_update_vsi *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_vsi_parameters);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
sizeof (vsi_ctx->info), NULL);
if (status)
goto aq_get_vsi_params_exit;
vsi_ctx->seid = le16_to_cpu(resp->seid);
vsi_ctx->vsi_number = le16_to_cpu(resp->vsi_number);
vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
aq_get_vsi_params_exit:
return status;
}
/**
* i40e_aq_update_vsi_params
* @ hw : pointer to the hw struct
* @ vsi_ctx : pointer to a vsi context struct
* @ cmd_details : pointer to command details structure or NULL
*
* Update a VSI context .
**/
int i40e_aq_update_vsi_params(struct i40e_hw *hw,
struct i40e_vsi_context *vsi_ctx,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_add_get_update_vsi_completion *resp;
struct i40e_aqc_add_get_update_vsi *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_update_vsi_parameters);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
status = i40e_asq_send_command_atomic(hw, &desc, &vsi_ctx->info,
sizeof (vsi_ctx->info),
cmd_details, true );
vsi_ctx->vsis_allocated = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
return status;
}
/**
* i40e_aq_get_switch_config
* @ hw : pointer to the hardware structure
* @ buf : pointer to the result buffer
* @ buf_size : length of input buffer
* @ start_seid : seid to start for the report , 0 = = beginning
* @ cmd_details : pointer to command details structure or NULL
*
* Fill the buf with switch configuration returned from AdminQ command
**/
int i40e_aq_get_switch_config(struct i40e_hw *hw,
struct i40e_aqc_get_switch_config_resp *buf,
u16 buf_size, u16 *start_seid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_switch_seid *scfg;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_switch_config);
scfg = libie_aq_raw(&desc);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (buf_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
scfg->seid = cpu_to_le16(*start_seid);
status = i40e_asq_send_command(hw, &desc, buf, buf_size, cmd_details);
*start_seid = le16_to_cpu(scfg->seid);
return status;
}
/**
* i40e_aq_set_switch_config
* @ hw : pointer to the hardware structure
* @ flags : bit flag values to set
* @ mode : cloud filter mode
* @ valid_flags : which bit flags to set
* @ mode : cloud filter mode
* @ cmd_details : pointer to command details structure or NULL
*
* Set switch configuration bits
**/
int i40e_aq_set_switch_config(struct i40e_hw *hw,
u16 flags,
u16 valid_flags, u8 mode,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_switch_config *scfg;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_switch_config);
scfg = libie_aq_raw(&desc);
scfg->flags = cpu_to_le16(flags);
scfg->valid_flags = cpu_to_le16(valid_flags);
scfg->mode = mode;
if (test_bit(I40E_HW_CAP_802_1AD, hw->caps)) {
scfg->switch_tag = cpu_to_le16(hw->switch_tag);
scfg->first_tag = cpu_to_le16(hw->first_tag);
scfg->second_tag = cpu_to_le16(hw->second_tag);
}
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_get_firmware_version
* @ hw : pointer to the hw struct
* @ fw_major_version : firmware major version
* @ fw_minor_version : firmware minor version
* @ fw_build : firmware build number
* @ api_major_version : major queue version
* @ api_minor_version : minor queue version
* @ cmd_details : pointer to command details structure or NULL
*
* Get the firmware version from the admin queue commands
**/
int i40e_aq_get_firmware_version(struct i40e_hw *hw,
u16 *fw_major_version, u16 *fw_minor_version,
u32 *fw_build,
u16 *api_major_version, u16 *api_minor_version,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_get_version *resp;
struct libie_aq_desc desc;
int status;
resp = libie_aq_raw(&desc);
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_version);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status) {
if (fw_major_version)
*fw_major_version = le16_to_cpu(resp->fw_major);
if (fw_minor_version)
*fw_minor_version = le16_to_cpu(resp->fw_minor);
if (fw_build)
*fw_build = le32_to_cpu(resp->fw_build);
if (api_major_version)
*api_major_version = le16_to_cpu(resp->api_major);
if (api_minor_version)
*api_minor_version = le16_to_cpu(resp->api_minor);
}
return status;
}
/**
* i40e_aq_send_driver_version
* @ hw : pointer to the hw struct
* @ dv : driver ' s major , minor version
* @ cmd_details : pointer to command details structure or NULL
*
* Send the driver version to the firmware
**/
int i40e_aq_send_driver_version(struct i40e_hw *hw,
struct i40e_driver_version *dv,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aqc_driver_ver *cmd;
struct libie_aq_desc desc;
int status;
u16 len;
if (dv == NULL)
return -EINVAL;
cmd = libie_aq_raw(&desc);
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_driver_version);
desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD);
cmd->major_ver = dv->major_version;
cmd->minor_ver = dv->minor_version;
cmd->build_ver = dv->build_version;
cmd->subbuild_ver = dv->subbuild_version;
len = 0 ;
while (len < sizeof (dv->driver_string) &&
(dv->driver_string[len] < 0 x80) &&
dv->driver_string[len])
len++;
status = i40e_asq_send_command(hw, &desc, dv->driver_string,
len, cmd_details);
return status;
}
/**
* i40e_get_link_status - get status of the HW network link
* @ hw : pointer to the hw struct
* @ link_up : pointer to bool ( true / false = linkup / linkdown )
*
* Variable link_up true if link is up , false if link is down .
* The variable link_up is invalid if returned value of status ! = 0
*
* Side effect : LinkStatusEvent reporting becomes enabled
**/
int i40e_get_link_status(struct i40e_hw *hw, bool *link_up)
{
int status = 0 ;
if (hw->phy.get_link_info) {
status = i40e_update_link_info(hw);
if (status)
i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n" ,
status);
}
*link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP;
return status;
}
/**
* i40e_update_link_info - update status of the HW network link
* @ hw : pointer to the hw struct
**/
noinline_for_stack int i40e_update_link_info(struct i40e_hw *hw)
{
struct i40e_aq_get_phy_abilities_resp abilities;
int status = 0 ;
status = i40e_aq_get_link_info(hw, true , NULL, NULL);
if (status)
return status;
/* extra checking needed to ensure link info to user is timely */
if ((hw->phy.link_info.link_info & I40E_AQ_MEDIA_AVAILABLE) &&
((hw->phy.link_info.link_info & I40E_AQ_LINK_UP) ||
!(hw->phy.link_info_old.link_info & I40E_AQ_LINK_UP))) {
status = i40e_aq_get_phy_capabilities(hw, false , false ,
&abilities, NULL);
if (status)
return status;
if (abilities.fec_cfg_curr_mod_ext_info &
I40E_AQ_ENABLE_FEC_AUTO)
hw->phy.link_info.req_fec_info =
(I40E_AQ_REQUEST_FEC_KR |
I40E_AQ_REQUEST_FEC_RS);
else
hw->phy.link_info.req_fec_info =
abilities.fec_cfg_curr_mod_ext_info &
(I40E_AQ_REQUEST_FEC_KR |
I40E_AQ_REQUEST_FEC_RS);
memcpy(hw->phy.link_info.module_type, &abilities.module_type,
sizeof (hw->phy.link_info.module_type));
}
return status;
}
/**
* i40e_aq_add_veb - Insert a VEB between the VSI and the MAC
* @ hw : pointer to the hw struct
* @ uplink_seid : the MAC or other gizmo SEID
* @ downlink_seid : the VSI SEID
* @ enabled_tc : bitmap of TCs to be enabled
* @ default_port : true for default port VSI , false for control port
* @ veb_seid : pointer to where to put the resulting VEB SEID
* @ enable_stats : true to turn on VEB stats
* @ cmd_details : pointer to command details structure or NULL
*
* This asks the FW to add a VEB between the uplink and downlink
* elements . If the uplink SEID is 0 , this will be a floating VEB .
**/
int i40e_aq_add_veb(struct i40e_hw *hw, u16 uplink_seid,
u16 downlink_seid, u8 enabled_tc,
bool default_port, u16 *veb_seid,
bool enable_stats,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_add_veb_completion *resp;
struct i40e_aqc_add_veb *cmd;
struct libie_aq_desc desc;
u16 veb_flags = 0 ;
int status;
/* SEIDs need to either both be set or both be 0 for floating VEB */
if (!!uplink_seid != !!downlink_seid)
return -EINVAL;
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_veb);
cmd->uplink_seid = cpu_to_le16(uplink_seid);
cmd->downlink_seid = cpu_to_le16(downlink_seid);
cmd->enable_tcs = enabled_tc;
if (!uplink_seid)
veb_flags |= I40E_AQC_ADD_VEB_FLOATING;
if (default_port)
veb_flags |= I40E_AQC_ADD_VEB_PORT_TYPE_DEFAULT;
else
veb_flags |= I40E_AQC_ADD_VEB_PORT_TYPE_DATA;
/* reverse logic here: set the bitflag to disable the stats */
if (!enable_stats)
veb_flags |= I40E_AQC_ADD_VEB_ENABLE_DISABLE_STATS;
cmd->veb_flags = cpu_to_le16(veb_flags);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status && veb_seid)
*veb_seid = le16_to_cpu(resp->veb_seid);
return status;
}
/**
* i40e_aq_get_veb_parameters - Retrieve VEB parameters
* @ hw : pointer to the hw struct
* @ veb_seid : the SEID of the VEB to query
* @ switch_id : the uplink switch id
* @ floating : set to true if the VEB is floating
* @ statistic_index : index of the stats counter block for this VEB
* @ vebs_used : number of VEB ' s used by function
* @ vebs_free : total VEB ' s not reserved by any function
* @ cmd_details : pointer to command details structure or NULL
*
* This retrieves the parameters for a particular VEB , specified by
* uplink_seid , and returns them to the caller .
**/
int i40e_aq_get_veb_parameters(struct i40e_hw *hw,
u16 veb_seid, u16 *switch_id,
bool *floating, u16 *statistic_index,
u16 *vebs_used, u16 *vebs_free,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_get_veb_parameters_completion *cmd_resp;
struct libie_aq_desc desc;
int status;
if (veb_seid == 0 )
return -EINVAL;
cmd_resp = libie_aq_raw(&desc);
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_veb_parameters);
cmd_resp->seid = cpu_to_le16(veb_seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (status)
goto get_veb_exit;
if (switch_id)
*switch_id = le16_to_cpu(cmd_resp->switch_id);
if (statistic_index)
*statistic_index = le16_to_cpu(cmd_resp->statistic_index);
if (vebs_used)
*vebs_used = le16_to_cpu(cmd_resp->vebs_used);
if (vebs_free)
*vebs_free = le16_to_cpu(cmd_resp->vebs_free);
if (floating) {
u16 flags = le16_to_cpu(cmd_resp->veb_flags);
if (flags & I40E_AQC_ADD_VEB_FLOATING)
*floating = true ;
else
*floating = false ;
}
get_veb_exit:
return status;
}
/**
* i40e_prepare_add_macvlan
* @ mv_list : list of macvlans to be added
* @ desc : pointer to AQ descriptor structure
* @ count : length of the list
* @ seid : VSI for the mac address
*
* Internal helper function that prepares the add macvlan request
* and returns the buffer size .
**/
static u16
i40e_prepare_add_macvlan(struct i40e_aqc_add_macvlan_element_data *mv_list,
struct libie_aq_desc *desc, u16 count, u16 seid)
{
struct i40e_aqc_macvlan *cmd = libie_aq_raw(desc);
u16 buf_size;
int i;
buf_size = count * sizeof (*mv_list);
/* prep the rest of the request */
i40e_fill_default_direct_cmd_desc(desc, i40e_aqc_opc_add_macvlan);
cmd->num_addresses = cpu_to_le16(count);
cmd->seid[0 ] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid);
cmd->seid[1 ] = 0 ;
cmd->seid[2 ] = 0 ;
for (i = 0 ; i < count; i++)
if (is_multicast_ether_addr(mv_list[i].mac_addr))
mv_list[i].flags |=
cpu_to_le16(I40E_AQC_MACVLAN_ADD_USE_SHARED_MAC);
desc->flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
if (buf_size > I40E_AQ_LARGE_BUF)
desc->flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
return buf_size;
}
/**
* i40e_aq_add_macvlan
* @ hw : pointer to the hw struct
* @ seid : VSI for the mac address
* @ mv_list : list of macvlans to be added
* @ count : length of the list
* @ cmd_details : pointer to command details structure or NULL
*
* Add MAC / VLAN addresses to the HW filtering
**/
int
i40e_aq_add_macvlan(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_add_macvlan_element_data *mv_list,
u16 count, struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aq_desc desc;
u16 buf_size;
if (count == 0 || !mv_list || !hw)
return -EINVAL;
buf_size = i40e_prepare_add_macvlan(mv_list, &desc, count, seid);
return i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size,
cmd_details, true );
}
/**
* i40e_aq_add_macvlan_v2
* @ hw : pointer to the hw struct
* @ seid : VSI for the mac address
* @ mv_list : list of macvlans to be added
* @ count : length of the list
* @ cmd_details : pointer to command details structure or NULL
* @ aq_status : pointer to Admin Queue status return value
*
* Add MAC / VLAN addresses to the HW filtering .
* The _ v2 version returns the last Admin Queue status in aq_status
* to avoid race conditions in access to hw - > aq . asq_last_status .
* It also calls _ v2 versions of asq_send_command functions to
* get the aq_status on the stack .
**/
int
i40e_aq_add_macvlan_v2(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_add_macvlan_element_data *mv_list,
u16 count, struct i40e_asq_cmd_details *cmd_details,
enum libie_aq_err *aq_status)
{
struct libie_aq_desc desc;
u16 buf_size;
if (count == 0 || !mv_list || !hw)
return -EINVAL;
buf_size = i40e_prepare_add_macvlan(mv_list, &desc, count, seid);
return i40e_asq_send_command_atomic_v2(hw, &desc, mv_list, buf_size,
cmd_details, true , aq_status);
}
/**
* i40e_aq_remove_macvlan
* @ hw : pointer to the hw struct
* @ seid : VSI for the mac address
* @ mv_list : list of macvlans to be removed
* @ count : length of the list
* @ cmd_details : pointer to command details structure or NULL
*
* Remove MAC / VLAN addresses from the HW filtering
**/
int
i40e_aq_remove_macvlan(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_remove_macvlan_element_data *mv_list,
u16 count, struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_macvlan *cmd;
struct libie_aq_desc desc;
u16 buf_size;
int status;
if (count == 0 || !mv_list || !hw)
return -EINVAL;
buf_size = count * sizeof (*mv_list);
/* prep the rest of the request */
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_remove_macvlan);
cmd = libie_aq_raw(&desc);
cmd->num_addresses = cpu_to_le16(count);
cmd->seid[0 ] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid);
cmd->seid[1 ] = 0 ;
cmd->seid[2 ] = 0 ;
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
if (buf_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
status = i40e_asq_send_command_atomic(hw, &desc, mv_list, buf_size,
cmd_details, true );
return status;
}
/**
* i40e_aq_remove_macvlan_v2
* @ hw : pointer to the hw struct
* @ seid : VSI for the mac address
* @ mv_list : list of macvlans to be removed
* @ count : length of the list
* @ cmd_details : pointer to command details structure or NULL
* @ aq_status : pointer to Admin Queue status return value
*
* Remove MAC / VLAN addresses from the HW filtering .
* The _ v2 version returns the last Admin Queue status in aq_status
* to avoid race conditions in access to hw - > aq . asq_last_status .
* It also calls _ v2 versions of asq_send_command functions to
* get the aq_status on the stack .
**/
int
i40e_aq_remove_macvlan_v2(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_remove_macvlan_element_data *mv_list,
u16 count, struct i40e_asq_cmd_details *cmd_details,
enum libie_aq_err *aq_status)
{
struct i40e_aqc_macvlan *cmd;
struct libie_aq_desc desc;
u16 buf_size;
if (count == 0 || !mv_list || !hw)
return -EINVAL;
buf_size = count * sizeof (*mv_list);
/* prep the rest of the request */
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_remove_macvlan);
cmd = libie_aq_raw(&desc);
cmd->num_addresses = cpu_to_le16(count);
cmd->seid[0 ] = cpu_to_le16(I40E_AQC_MACVLAN_CMD_SEID_VALID | seid);
cmd->seid[1 ] = 0 ;
cmd->seid[2 ] = 0 ;
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
if (buf_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
return i40e_asq_send_command_atomic_v2(hw, &desc, mv_list, buf_size,
cmd_details, true , aq_status);
}
/**
* i40e_aq_send_msg_to_vf
* @ hw : pointer to the hardware structure
* @ vfid : VF id to send msg
* @ v_opcode : opcodes for VF - PF communication
* @ v_retval : return error code
* @ msg : pointer to the msg buffer
* @ msglen : msg length
* @ cmd_details : pointer to command details
*
* send msg to vf
**/
int i40e_aq_send_msg_to_vf(struct i40e_hw *hw, u16 vfid,
u32 v_opcode, u32 v_retval, u8 *msg, u16 msglen,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_pf_vf_message *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_send_msg_to_vf);
cmd = libie_aq_raw(&desc);
cmd->id = cpu_to_le32(vfid);
desc.cookie_high = cpu_to_le32(v_opcode);
desc.cookie_low = cpu_to_le32(v_retval);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_SI);
if (msglen) {
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF |
LIBIE_AQ_FLAG_RD));
if (msglen > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(msglen);
}
status = i40e_asq_send_command(hw, &desc, msg, msglen, cmd_details);
return status;
}
/**
* i40e_aq_debug_read_register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
* @ reg_val : register value
* @ cmd_details : pointer to command details structure or NULL
*
* Read the register using the admin queue commands
**/
int i40e_aq_debug_read_register(struct i40e_hw *hw,
u32 reg_addr, u64 *reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_debug_reg_read_write *cmd_resp;
struct libie_aq_desc desc;
int status;
if (reg_val == NULL)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_debug_read_reg);
cmd_resp = libie_aq_raw(&desc);
cmd_resp->address = cpu_to_le32(reg_addr);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status) {
*reg_val = ((u64)le32_to_cpu(cmd_resp->value_high) << 32 ) |
(u64)le32_to_cpu(cmd_resp->value_low);
}
return status;
}
/**
* i40e_aq_debug_write_register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
* @ reg_val : register value
* @ cmd_details : pointer to command details structure or NULL
*
* Write to a register using the admin queue commands
**/
int i40e_aq_debug_write_register(struct i40e_hw *hw,
u32 reg_addr, u64 reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_debug_reg_read_write *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_debug_write_reg);
cmd = libie_aq_raw(&desc);
cmd->address = cpu_to_le32(reg_addr);
cmd->value_high = cpu_to_le32((u32)(reg_val >> 32 ));
cmd->value_low = cpu_to_le32((u32)(reg_val & 0 xFFFFFFFF));
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_request_resource
* @ hw : pointer to the hw struct
* @ resource : resource id
* @ access : access type
* @ sdp_number : resource number
* @ timeout : the maximum time in ms that the driver may hold the resource
* @ cmd_details : pointer to command details structure or NULL
*
* requests common resource using the admin queue commands
**/
int i40e_aq_request_resource(struct i40e_hw *hw,
enum i40e_aq_resources_ids resource,
enum i40e_aq_resource_access_type access,
u8 sdp_number, u64 *timeout,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aqc_req_res *cmd_resp;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_request_resource);
cmd_resp = libie_aq_raw(&desc);
cmd_resp->res_id = cpu_to_le16(resource);
cmd_resp->access_type = cpu_to_le16(access);
cmd_resp->res_number = cpu_to_le32(sdp_number);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
/* The completion specifies the maximum time in ms that the driver
* may hold the resource in the Timeout field .
* If the resource is held by someone else , the command completes with
* busy return value and the timeout field indicates the maximum time
* the current owner of the resource has to free it .
*/
if (!status || hw->aq.asq_last_status == LIBIE_AQ_RC_EBUSY)
*timeout = le32_to_cpu(cmd_resp->timeout);
return status;
}
/**
* i40e_aq_release_resource
* @ hw : pointer to the hw struct
* @ resource : resource id
* @ sdp_number : resource number
* @ cmd_details : pointer to command details structure or NULL
*
* release common resource using the admin queue commands
**/
int i40e_aq_release_resource(struct i40e_hw *hw,
enum i40e_aq_resources_ids resource,
u8 sdp_number,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aqc_req_res *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_release_resource);
cmd = libie_aq_raw(&desc);
cmd->res_id = cpu_to_le16(resource);
cmd->res_number = cpu_to_le32(sdp_number);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_read_nvm
* @ hw : pointer to the hw struct
* @ module_pointer : module pointer location in words from the NVM beginning
* @ offset : byte offset from the module beginning
* @ length : length of the section to be read ( in bytes from the offset )
* @ data : command buffer ( size [ bytes ] = length )
* @ last_command : tells if this is the last command in a series
* @ cmd_details : pointer to command details structure or NULL
*
* Read the NVM using the admin queue commands
**/
int i40e_aq_read_nvm(struct i40e_hw *hw, u8 module_pointer,
u32 offset, u16 length, void *data,
bool last_command,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_nvm_update *cmd;
struct libie_aq_desc desc;
int status;
/* In offset the highest byte must be zeroed. */
if (offset & 0 xFF000000) {
status = -EINVAL;
goto i40e_aq_read_nvm_exit;
}
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_read);
cmd = libie_aq_raw(&desc);
/* If this is the last command in a series, set the proper flag. */
if (last_command)
cmd->command_flags |= I40E_AQ_NVM_LAST_CMD;
cmd->module_pointer = module_pointer;
cmd->offset = cpu_to_le32(offset);
cmd->length = cpu_to_le16(length);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (length > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
status = i40e_asq_send_command(hw, &desc, data, length, cmd_details);
i40e_aq_read_nvm_exit:
return status;
}
/**
* i40e_aq_erase_nvm
* @ hw : pointer to the hw struct
* @ module_pointer : module pointer location in words from the NVM beginning
* @ offset : offset in the module ( expressed in 4 KB from module ' s beginning )
* @ length : length of the section to be erased ( expressed in 4 KB )
* @ last_command : tells if this is the last command in a series
* @ cmd_details : pointer to command details structure or NULL
*
* Erase the NVM sector using the admin queue commands
**/
int i40e_aq_erase_nvm(struct i40e_hw *hw, u8 module_pointer,
u32 offset, u16 length, bool last_command,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_nvm_update *cmd;
struct libie_aq_desc desc;
int status;
/* In offset the highest byte must be zeroed. */
if (offset & 0 xFF000000) {
status = -EINVAL;
goto i40e_aq_erase_nvm_exit;
}
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_erase);
cmd = libie_aq_raw(&desc);
/* If this is the last command in a series, set the proper flag. */
if (last_command)
cmd->command_flags |= I40E_AQ_NVM_LAST_CMD;
cmd->module_pointer = module_pointer;
cmd->offset = cpu_to_le32(offset);
cmd->length = cpu_to_le16(length);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
i40e_aq_erase_nvm_exit:
return status;
}
/**
* i40e_parse_discover_capabilities
* @ hw : pointer to the hw struct
* @ buff : pointer to a buffer containing device / function capability records
* @ cap_count : number of capability records in the list
* @ list_type_opc : type of capabilities list to parse
*
* Parse the device / function capabilities list .
**/
static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
u32 cap_count,
enum i40e_admin_queue_opc list_type_opc)
{
struct libie_aqc_list_caps_elem *cap;
u32 valid_functions, num_functions;
u32 number, logical_id, phys_id;
struct i40e_hw_capabilities *p;
u16 id, ocp_cfg_word0;
u8 major_rev;
int status;
u32 i = 0 ;
cap = (struct libie_aqc_list_caps_elem *)buff;
if (list_type_opc == i40e_aqc_opc_list_dev_capabilities)
p = &hw->dev_caps;
else if (list_type_opc == i40e_aqc_opc_list_func_capabilities)
p = &hw->func_caps;
else
return ;
for (i = 0 ; i < cap_count; i++, cap++) {
id = le16_to_cpu(cap->cap);
number = le32_to_cpu(cap->number);
logical_id = le32_to_cpu(cap->logical_id);
phys_id = le32_to_cpu(cap->phys_id);
major_rev = cap->major_ver;
switch (id) {
case LIBIE_AQC_CAPS_SWITCH_MODE:
p->switch_mode = number;
break ;
case LIBIE_AQC_CAPS_MNG_MODE:
p->management_mode = number;
if (major_rev > 1 ) {
p->mng_protocols_over_mctp = logical_id;
i40e_debug(hw, I40E_DEBUG_INIT,
"HW Capability: Protocols over MCTP = %d\n" ,
p->mng_protocols_over_mctp);
} else {
p->mng_protocols_over_mctp = 0 ;
}
break ;
case LIBIE_AQC_CAPS_NPAR_ACTIVE:
p->npar_enable = number;
break ;
case LIBIE_AQC_CAPS_OS2BMC_CAP:
p->os2bmc = number;
break ;
case LIBIE_AQC_CAPS_VALID_FUNCTIONS:
p->valid_functions = number;
break ;
case LIBIE_AQC_CAPS_SRIOV:
if (number == 1 )
p->sr_iov_1_1 = true ;
break ;
case LIBIE_AQC_CAPS_VF:
p->num_vfs = number;
p->vf_base_id = logical_id;
break ;
case LIBIE_AQC_CAPS_VMDQ:
if (number == 1 )
p->vmdq = true ;
break ;
case LIBIE_AQC_CAPS_8021QBG:
if (number == 1 )
p->evb_802_1_qbg = true ;
break ;
case LIBIE_AQC_CAPS_8021QBR:
if (number == 1 )
p->evb_802_1_qbh = true ;
break ;
case LIBIE_AQC_CAPS_VSI:
p->num_vsis = number;
break ;
case LIBIE_AQC_CAPS_DCB:
if (number == 1 ) {
p->dcb = true ;
p->enabled_tcmap = logical_id;
p->maxtc = phys_id;
}
break ;
case LIBIE_AQC_CAPS_FCOE:
if (number == 1 )
p->fcoe = true ;
break ;
case LIBIE_AQC_CAPS_ISCSI:
if (number == 1 )
p->iscsi = true ;
break ;
case LIBIE_AQC_CAPS_RSS:
p->rss = true ;
p->rss_table_size = number;
p->rss_table_entry_width = logical_id;
break ;
case LIBIE_AQC_CAPS_RXQS:
p->num_rx_qp = number;
p->base_queue = phys_id;
break ;
case LIBIE_AQC_CAPS_TXQS:
p->num_tx_qp = number;
p->base_queue = phys_id;
break ;
case LIBIE_AQC_CAPS_MSIX:
p->num_msix_vectors = number;
i40e_debug(hw, I40E_DEBUG_INIT,
"HW Capability: MSIX vector count = %d\n" ,
p->num_msix_vectors);
break ;
case LIBIE_AQC_CAPS_VF_MSIX:
p->num_msix_vectors_vf = number;
break ;
case LIBIE_AQC_CAPS_FLEX10:
if (major_rev == 1 ) {
if (number == 1 ) {
p->flex10_enable = true ;
p->flex10_capable = true ;
}
} else {
/* Capability revision >= 2 */
if (number & 1 )
p->flex10_enable = true ;
if (number & 2 )
p->flex10_capable = true ;
}
p->flex10_mode = logical_id;
p->flex10_status = phys_id;
break ;
case LIBIE_AQC_CAPS_CEM:
if (number == 1 )
p->mgmt_cem = true ;
break ;
case LIBIE_AQC_CAPS_RDMA:
if (number == 1 )
p->iwarp = true ;
break ;
case LIBIE_AQC_CAPS_LED:
if (phys_id < I40E_HW_CAP_MAX_GPIO)
p->led[phys_id] = true ;
break ;
case LIBIE_AQC_CAPS_SDP:
if (phys_id < I40E_HW_CAP_MAX_GPIO)
p->sdp[phys_id] = true ;
break ;
case LIBIE_AQC_CAPS_MDIO:
if (number == 1 ) {
p->mdio_port_num = phys_id;
p->mdio_port_mode = logical_id;
}
break ;
case LIBIE_AQC_CAPS_1588:
if (number == 1 )
p->ieee_1588 = true ;
break ;
case LIBIE_AQC_CAPS_FD:
p->fd = true ;
p->fd_filters_guaranteed = number;
p->fd_filters_best_effort = logical_id;
break ;
case LIBIE_AQC_CAPS_WSR_PROT:
p->wr_csr_prot = (u64)number;
p->wr_csr_prot |= (u64)logical_id << 32 ;
break ;
case LIBIE_AQC_CAPS_NVM_MGMT:
if (number & I40E_NVM_MGMT_SEC_REV_DISABLED)
p->sec_rev_disabled = true ;
if (number & I40E_NVM_MGMT_UPDATE_DISABLED)
p->update_disabled = true ;
break ;
default :
break ;
}
}
if (p->fcoe)
i40e_debug(hw, I40E_DEBUG_ALL, "device is FCoE capable\n" );
/* Software override ensuring FCoE is disabled if npar or mfp
* mode because it is not supported in these modes .
*/
if (p->npar_enable || p->flex10_enable)
p->fcoe = false ;
/* count the enabled ports (aka the "not disabled" ports) */
hw->num_ports = 0 ;
for (i = 0 ; i < 4 ; i++) {
u32 port_cfg_reg = I40E_PRTGEN_CNF + (4 * i);
u64 port_cfg = 0 ;
/* use AQ read to get the physical register offset instead
* of the port relative offset
*/
i40e_aq_debug_read_register(hw, port_cfg_reg, &port_cfg, NULL);
if (!(port_cfg & I40E_PRTGEN_CNF_PORT_DIS_MASK))
hw->num_ports++;
}
/* OCP cards case: if a mezz is removed the Ethernet port is at
* disabled state in PRTGEN_CNF register . Additional NVM read is
* needed in order to check if we are dealing with OCP card .
* Those cards have 4 PFs at minimum , so using PRTGEN_CNF for counting
* physical ports results in wrong partition id calculation and thus
* not supporting WoL .
*/
if (hw->mac.type == I40E_MAC_X722) {
if (!i40e_acquire_nvm(hw, I40E_RESOURCE_READ)) {
status = i40e_aq_read_nvm(hw, I40E_SR_EMP_MODULE_PTR,
2 * I40E_SR_OCP_CFG_WORD0,
sizeof (ocp_cfg_word0),
&ocp_cfg_word0, true , NULL);
if (!status &&
(ocp_cfg_word0 & I40E_SR_OCP_ENABLED))
hw->num_ports = 4 ;
i40e_release_nvm(hw);
}
}
valid_functions = p->valid_functions;
num_functions = 0 ;
while (valid_functions) {
if (valid_functions & 1 )
num_functions++;
valid_functions >>= 1 ;
}
/* partition id is 1-based, and functions are evenly spread
* across the ports as partitions
*/
if (hw->num_ports != 0 ) {
hw->partition_id = (hw->pf_id / hw->num_ports) + 1 ;
hw->num_partitions = num_functions / hw->num_ports;
}
/* additional HW specific goodies that might
* someday be HW version specific
*/
p->rx_buf_chain_len = I40E_MAX_CHAINED_RX_BUFFERS;
}
/**
* i40e_aq_discover_capabilities
* @ hw : pointer to the hw struct
* @ buff : a virtual buffer to hold the capabilities
* @ buff_size : Size of the virtual buffer
* @ data_size : Size of the returned data , or buff size needed if AQ err = = ENOMEM
* @ list_type_opc : capabilities type to discover - pass in the command opcode
* @ cmd_details : pointer to command details structure or NULL
*
* Get the device capabilities descriptions from the firmware
**/
int i40e_aq_discover_capabilities(struct i40e_hw *hw,
void *buff, u16 buff_size, u16 *data_size,
enum i40e_admin_queue_opc list_type_opc,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aqc_list_caps *cmd;
struct libie_aq_desc desc;
int status = 0 ;
cmd = libie_aq_raw(&desc);
if (list_type_opc != i40e_aqc_opc_list_func_capabilities &&
list_type_opc != i40e_aqc_opc_list_dev_capabilities) {
status = -EINVAL;
goto exit ;
}
i40e_fill_default_direct_cmd_desc(&desc, list_type_opc);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
*data_size = le16_to_cpu(desc.datalen);
if (status)
goto exit ;
i40e_parse_discover_capabilities(hw, buff, le32_to_cpu(cmd->count),
list_type_opc);
exit :
return status;
}
/**
* i40e_aq_update_nvm
* @ hw : pointer to the hw struct
* @ module_pointer : module pointer location in words from the NVM beginning
* @ offset : byte offset from the module beginning
* @ length : length of the section to be written ( in bytes from the offset )
* @ data : command buffer ( size [ bytes ] = length )
* @ last_command : tells if this is the last command in a series
* @ preservation_flags : Preservation mode flags
* @ cmd_details : pointer to command details structure or NULL
*
* Update the NVM using the admin queue commands
**/
int i40e_aq_update_nvm(struct i40e_hw *hw, u8 module_pointer,
u32 offset, u16 length, void *data,
bool last_command, u8 preservation_flags,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_nvm_update *cmd;
struct libie_aq_desc desc;
int status;
/* In offset the highest byte must be zeroed. */
if (offset & 0 xFF000000) {
status = -EINVAL;
goto i40e_aq_update_nvm_exit;
}
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_nvm_update);
cmd = libie_aq_raw(&desc);
/* If this is the last command in a series, set the proper flag. */
if (last_command)
cmd->command_flags |= I40E_AQ_NVM_LAST_CMD;
if (hw->mac.type == I40E_MAC_X722) {
if (preservation_flags == I40E_NVM_PRESERVATION_FLAGS_SELECTED)
cmd->command_flags |=
(I40E_AQ_NVM_PRESERVATION_FLAGS_SELECTED <<
I40E_AQ_NVM_PRESERVATION_FLAGS_SHIFT);
else if (preservation_flags == I40E_NVM_PRESERVATION_FLAGS_ALL)
cmd->command_flags |=
(I40E_AQ_NVM_PRESERVATION_FLAGS_ALL <<
I40E_AQ_NVM_PRESERVATION_FLAGS_SHIFT);
}
cmd->module_pointer = module_pointer;
cmd->offset = cpu_to_le32(offset);
cmd->length = cpu_to_le16(length);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
if (length > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
status = i40e_asq_send_command(hw, &desc, data, length, cmd_details);
i40e_aq_update_nvm_exit:
return status;
}
/**
* i40e_aq_get_lldp_mib
* @ hw : pointer to the hw struct
* @ bridge_type : type of bridge requested
* @ mib_type : Local , Remote or both Local and Remote MIBs
* @ buff : pointer to a user supplied buffer to store the MIB block
* @ buff_size : size of the buffer ( in bytes )
* @ local_len : length of the returned Local LLDP MIB
* @ remote_len : length of the returned Remote LLDP MIB
* @ cmd_details : pointer to command details structure or NULL
*
* Requests the complete LLDP MIB ( entire packet ) .
**/
int i40e_aq_get_lldp_mib(struct i40e_hw *hw, u8 bridge_type,
u8 mib_type, void *buff, u16 buff_size,
u16 *local_len, u16 *remote_len,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_lldp_get_mib *resp;
struct i40e_aqc_lldp_get_mib *cmd;
struct libie_aq_desc desc;
int status;
if (buff_size == 0 || !buff)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_get_mib);
/* Indirect Command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
cmd->type = mib_type & I40E_AQ_LLDP_MIB_TYPE_MASK;
cmd->type |= FIELD_PREP(I40E_AQ_LLDP_BRIDGE_TYPE_MASK, bridge_type);
desc.datalen = cpu_to_le16(buff_size);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
if (!status) {
if (local_len != NULL)
*local_len = le16_to_cpu(resp->local_len);
if (remote_len != NULL)
*remote_len = le16_to_cpu(resp->remote_len);
}
return status;
}
/**
* i40e_aq_set_lldp_mib - Set the LLDP MIB
* @ hw : pointer to the hw struct
* @ mib_type : Local , Remote or both Local and Remote MIBs
* @ buff : pointer to a user supplied buffer to store the MIB block
* @ buff_size : size of the buffer ( in bytes )
* @ cmd_details : pointer to command details structure or NULL
*
* Set the LLDP MIB .
**/
int
i40e_aq_set_lldp_mib(struct i40e_hw *hw,
u8 mib_type, void *buff, u16 buff_size,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_lldp_set_local_mib *cmd;
struct libie_aq_desc desc;
int status;
cmd = libie_aq_raw(&desc);
if (buff_size == 0 || !buff)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_lldp_set_local_mib);
/* Indirect Command */
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(buff_size);
cmd->type = mib_type;
cmd->length = cpu_to_le16(buff_size);
cmd->address_high = cpu_to_le32(upper_32_bits((uintptr_t)buff));
cmd->address_low = cpu_to_le32(lower_32_bits((uintptr_t)buff));
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
return status;
}
/**
* i40e_aq_cfg_lldp_mib_change_event
* @ hw : pointer to the hw struct
* @ enable_update : Enable or Disable event posting
* @ cmd_details : pointer to command details structure or NULL
*
* Enable or Disable posting of an event on ARQ when LLDP MIB
* associated with the interface changes
**/
int i40e_aq_cfg_lldp_mib_change_event(struct i40e_hw *hw,
bool enable_update,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_lldp_update_mib *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_update_mib);
cmd = libie_aq_raw(&desc);
if (!enable_update)
cmd->command |= I40E_AQ_LLDP_MIB_UPDATE_DISABLE;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_stop_lldp
* @ hw : pointer to the hw struct
* @ shutdown_agent : True if LLDP Agent needs to be Shutdown
* @ persist : True if stop of LLDP should be persistent across power cycles
* @ cmd_details : pointer to command details structure or NULL
*
* Stop or Shutdown the embedded LLDP Agent
**/
int i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent,
bool persist,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_lldp_stop *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_stop);
cmd = libie_aq_raw(&desc);
if (shutdown_agent)
cmd->command |= I40E_AQ_LLDP_AGENT_SHUTDOWN;
if (persist) {
if (test_bit(I40E_HW_CAP_FW_LLDP_PERSISTENT, hw->caps))
cmd->command |= I40E_AQ_LLDP_AGENT_STOP_PERSIST;
else
i40e_debug(hw, I40E_DEBUG_ALL,
"Persistent Stop LLDP not supported by current FW version.\n" );
}
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_start_lldp
* @ hw : pointer to the hw struct
* @ persist : True if start of LLDP should be persistent across power cycles
* @ cmd_details : pointer to command details structure or NULL
*
* Start the embedded LLDP Agent on all ports .
**/
int i40e_aq_start_lldp(struct i40e_hw *hw, bool persist,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_lldp_start *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_lldp_start);
cmd = libie_aq_raw(&desc);
cmd->command = I40E_AQ_LLDP_AGENT_START;
if (persist) {
if (test_bit(I40E_HW_CAP_FW_LLDP_PERSISTENT, hw->caps))
cmd->command |= I40E_AQ_LLDP_AGENT_START_PERSIST;
else
i40e_debug(hw, I40E_DEBUG_ALL,
"Persistent Start LLDP not supported by current FW version.\n" );
}
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_set_dcb_parameters
* @ hw : pointer to the hw struct
* @ cmd_details : pointer to command details structure or NULL
* @ dcb_enable : True if DCB configuration needs to be applied
*
**/
int
i40e_aq_set_dcb_parameters(struct i40e_hw *hw, bool dcb_enable,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_set_dcb_parameters *cmd;
struct libie_aq_desc desc;
int status;
if (!test_bit(I40E_HW_CAP_FW_LLDP_STOPPABLE, hw->caps))
return -ENODEV;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_dcb_parameters);
cmd = libie_aq_raw(&desc);
if (dcb_enable) {
cmd->valid_flags = I40E_DCB_VALID;
cmd->command = I40E_AQ_DCB_SET_AGENT;
}
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_get_cee_dcb_config
* @ hw : pointer to the hw struct
* @ buff : response buffer that stores CEE operational configuration
* @ buff_size : size of the buffer passed
* @ cmd_details : pointer to command details structure or NULL
*
* Get CEE DCBX mode operational configuration from firmware
**/
int i40e_aq_get_cee_dcb_config(struct i40e_hw *hw,
void *buff, u16 buff_size,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aq_desc desc;
int status;
if (buff_size == 0 || !buff)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_get_cee_dcb_cfg);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
status = i40e_asq_send_command(hw, &desc, (void *)buff, buff_size,
cmd_details);
return status;
}
/**
* i40e_aq_add_udp_tunnel
* @ hw : pointer to the hw struct
* @ udp_port : the UDP port to add in Host byte order
* @ protocol_index : protocol index type
* @ filter_index : pointer to filter index
* @ cmd_details : pointer to command details structure or NULL
*
* Note : Firmware expects the udp_port value to be in Little Endian format ,
* and this function will call cpu_to_le16 to convert from Host byte order to
* Little Endian order .
**/
int i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
u16 udp_port, u8 protocol_index,
u8 *filter_index,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_del_udp_tunnel_completion *resp;
struct i40e_aqc_add_udp_tunnel *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_add_udp_tunnel);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
cmd->udp_port = cpu_to_le16(udp_port);
cmd->protocol_type = protocol_index;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status && filter_index)
*filter_index = resp->index;
return status;
}
/**
* i40e_aq_del_udp_tunnel
* @ hw : pointer to the hw struct
* @ index : filter index
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_remove_udp_tunnel *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_del_udp_tunnel);
cmd = libie_aq_raw(&desc);
cmd->index = index;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_delete_element - Delete switch element
* @ hw : pointer to the hw struct
* @ seid : the SEID to delete from the switch
* @ cmd_details : pointer to command details structure or NULL
*
* This deletes a switch element from the switch .
**/
int i40e_aq_delete_element(struct i40e_hw *hw, u16 seid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_switch_seid *cmd;
struct libie_aq_desc desc;
int status;
if (seid == 0 )
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_delete_element);
cmd = libie_aq_raw(&desc);
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command_atomic(hw, &desc, NULL, 0 ,
cmd_details, true );
return status;
}
/**
* i40e_aq_dcb_updated - DCB Updated Command
* @ hw : pointer to the hw struct
* @ cmd_details : pointer to command details structure or NULL
*
* EMP will return when the shared RPB settings have been
* recomputed and modified . The retval field in the descriptor
* will be set to 0 when RPB is modified .
**/
int i40e_aq_dcb_updated(struct i40e_hw *hw,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_dcb_updated);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_tx_sched_cmd - generic Tx scheduler AQ command handler
* @ hw : pointer to the hw struct
* @ seid : seid for the physical port / switching component / vsi
* @ buff : Indirect buffer to hold data parameters and response
* @ buff_size : Indirect buffer size
* @ opcode : Tx scheduler AQ command opcode
* @ cmd_details : pointer to command details structure or NULL
*
* Generic command handler for Tx scheduler AQ commands
**/
static int i40e_aq_tx_sched_cmd(struct i40e_hw *hw, u16 seid,
void *buff, u16 buff_size,
enum i40e_admin_queue_opc opcode,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_tx_sched_ind *cmd;
struct libie_aq_desc desc;
int status;
bool cmd_param_flag = false ;
switch (opcode) {
case i40e_aqc_opc_configure_vsi_ets_sla_bw_limit:
case i40e_aqc_opc_configure_vsi_tc_bw:
case i40e_aqc_opc_enable_switching_comp_ets:
case i40e_aqc_opc_modify_switching_comp_ets:
case i40e_aqc_opc_disable_switching_comp_ets:
case i40e_aqc_opc_configure_switching_comp_ets_bw_limit:
case i40e_aqc_opc_configure_switching_comp_bw_config:
cmd_param_flag = true ;
break ;
case i40e_aqc_opc_query_vsi_bw_config:
case i40e_aqc_opc_query_vsi_ets_sla_config:
case i40e_aqc_opc_query_switching_comp_ets_config:
case i40e_aqc_opc_query_port_ets_config:
case i40e_aqc_opc_query_switching_comp_bw_config:
cmd_param_flag = false ;
break ;
default :
return -EINVAL;
}
i40e_fill_default_direct_cmd_desc(&desc, opcode);
cmd = libie_aq_raw(&desc);
/* Indirect command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (cmd_param_flag)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_RD);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(buff_size);
cmd->vsi_seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
return status;
}
/**
* i40e_aq_config_vsi_bw_limit - Configure VSI BW Limit
* @ hw : pointer to the hw struct
* @ seid : VSI seid
* @ credit : BW limit credits ( 0 = disabled )
* @ max_credit : Max BW limit credits
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw,
u16 seid, u16 credit, u8 max_credit,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_configure_vsi_bw_limit *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_configure_vsi_bw_limit);
cmd = libie_aq_raw(&desc);
cmd->vsi_seid = cpu_to_le16(seid);
cmd->credit = cpu_to_le16(credit);
cmd->max_credit = max_credit;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_config_vsi_tc_bw - Config VSI BW Allocation per TC
* @ hw : pointer to the hw struct
* @ seid : VSI seid
* @ bw_data : Buffer holding enabled TCs , relative TC BW limit / credits
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_config_vsi_tc_bw(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_configure_vsi_tc_bw_data *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_configure_vsi_tc_bw,
cmd_details);
}
/**
* i40e_aq_config_switch_comp_ets - Enable / Disable / Modify ETS on the port
* @ hw : pointer to the hw struct
* @ seid : seid of the switching component connected to Physical Port
* @ ets_data : Buffer holding ETS parameters
* @ opcode : Tx scheduler AQ command opcode
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_config_switch_comp_ets(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_configure_switching_comp_ets_data *ets_data,
enum i40e_admin_queue_opc opcode,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)ets_data,
sizeof (*ets_data), opcode, cmd_details);
}
/**
* i40e_aq_config_switch_comp_bw_config - Config Switch comp BW Alloc per TC
* @ hw : pointer to the hw struct
* @ seid : seid of the switching component
* @ bw_data : Buffer holding enabled TCs , relative / absolute TC BW limit / credits
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_config_switch_comp_bw_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_configure_switching_comp_bw_config_data *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_configure_switching_comp_bw_config,
cmd_details);
}
/**
* i40e_aq_query_vsi_bw_config - Query VSI BW configuration
* @ hw : pointer to the hw struct
* @ seid : seid of the VSI
* @ bw_data : Buffer to hold VSI BW configuration
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_query_vsi_bw_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_query_vsi_bw_config_resp *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_query_vsi_bw_config,
cmd_details);
}
/**
* i40e_aq_query_vsi_ets_sla_config - Query VSI BW configuration per TC
* @ hw : pointer to the hw struct
* @ seid : seid of the VSI
* @ bw_data : Buffer to hold VSI BW configuration per TC
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_query_vsi_ets_sla_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_query_vsi_ets_sla_config_resp *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_query_vsi_ets_sla_config,
cmd_details);
}
/**
* i40e_aq_query_switch_comp_ets_config - Query Switch comp BW config per TC
* @ hw : pointer to the hw struct
* @ seid : seid of the switching component
* @ bw_data : Buffer to hold switching component ' s per TC BW config
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_query_switch_comp_ets_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_query_switching_comp_ets_config_resp *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_query_switching_comp_ets_config,
cmd_details);
}
/**
* i40e_aq_query_port_ets_config - Query Physical Port ETS configuration
* @ hw : pointer to the hw struct
* @ seid : seid of the VSI or switching component connected to Physical Port
* @ bw_data : Buffer to hold current ETS configuration for the Physical Port
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_query_port_ets_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_query_port_ets_config_resp *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_query_port_ets_config,
cmd_details);
}
/**
* i40e_aq_query_switch_comp_bw_config - Query Switch comp BW configuration
* @ hw : pointer to the hw struct
* @ seid : seid of the switching component
* @ bw_data : Buffer to hold switching component ' s BW configuration
* @ cmd_details : pointer to command details structure or NULL
**/
int
i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
u16 seid,
struct i40e_aqc_query_switching_comp_bw_config_resp *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
return i40e_aq_tx_sched_cmd(hw, seid, (void *)bw_data, sizeof (*bw_data),
i40e_aqc_opc_query_switching_comp_bw_config,
cmd_details);
}
/**
* i40e_validate_filter_settings
* @ hw : pointer to the hardware structure
* @ settings : Filter control settings
*
* Check and validate the filter control settings passed .
* The function checks for the valid filter / context sizes being
* passed for FCoE and PE .
*
* Returns 0 if the values passed are valid and within
* range else returns an error .
**/
static int
i40e_validate_filter_settings(struct i40e_hw *hw,
struct i40e_filter_control_settings *settings)
{
u32 fcoe_cntx_size, fcoe_filt_size;
u32 fcoe_fmax;
u32 val;
/* Validate FCoE settings passed */
switch (settings->fcoe_filt_num) {
case I40E_HASH_FILTER_SIZE_1K:
case I40E_HASH_FILTER_SIZE_2K:
case I40E_HASH_FILTER_SIZE_4K:
case I40E_HASH_FILTER_SIZE_8K:
case I40E_HASH_FILTER_SIZE_16K:
case I40E_HASH_FILTER_SIZE_32K:
fcoe_filt_size = I40E_HASH_FILTER_BASE_SIZE;
fcoe_filt_size <<= (u32)settings->fcoe_filt_num;
break ;
default :
return -EINVAL;
}
switch (settings->fcoe_cntx_num) {
case I40E_DMA_CNTX_SIZE_512:
case I40E_DMA_CNTX_SIZE_1K:
case I40E_DMA_CNTX_SIZE_2K:
case I40E_DMA_CNTX_SIZE_4K:
fcoe_cntx_size = I40E_DMA_CNTX_BASE_SIZE;
fcoe_cntx_size <<= (u32)settings->fcoe_cntx_num;
break ;
default :
return -EINVAL;
}
/* Validate PE settings passed */
switch (settings->pe_filt_num) {
case I40E_HASH_FILTER_SIZE_1K:
case I40E_HASH_FILTER_SIZE_2K:
case I40E_HASH_FILTER_SIZE_4K:
case I40E_HASH_FILTER_SIZE_8K:
case I40E_HASH_FILTER_SIZE_16K:
case I40E_HASH_FILTER_SIZE_32K:
case I40E_HASH_FILTER_SIZE_64K:
case I40E_HASH_FILTER_SIZE_128K:
case I40E_HASH_FILTER_SIZE_256K:
case I40E_HASH_FILTER_SIZE_512K:
case I40E_HASH_FILTER_SIZE_1M:
break ;
default :
return -EINVAL;
}
switch (settings->pe_cntx_num) {
case I40E_DMA_CNTX_SIZE_512:
case I40E_DMA_CNTX_SIZE_1K:
case I40E_DMA_CNTX_SIZE_2K:
case I40E_DMA_CNTX_SIZE_4K:
case I40E_DMA_CNTX_SIZE_8K:
case I40E_DMA_CNTX_SIZE_16K:
case I40E_DMA_CNTX_SIZE_32K:
case I40E_DMA_CNTX_SIZE_64K:
case I40E_DMA_CNTX_SIZE_128K:
case I40E_DMA_CNTX_SIZE_256K:
break ;
default :
return -EINVAL;
}
/* FCHSIZE + FCDSIZE should not be greater than PMFCOEFMAX */
val = rd32(hw, I40E_GLHMC_FCOEFMAX);
fcoe_fmax = FIELD_GET(I40E_GLHMC_FCOEFMAX_PMFCOEFMAX_MASK, val);
if (fcoe_filt_size + fcoe_cntx_size > fcoe_fmax)
return -EINVAL;
return 0 ;
}
/**
* i40e_set_filter_control
* @ hw : pointer to the hardware structure
* @ settings : Filter control settings
*
* Set the Queue Filters for PE / FCoE and enable filters required
* for a single PF . It is expected that these settings are programmed
* at the driver initialization time .
**/
int i40e_set_filter_control(struct i40e_hw *hw,
struct i40e_filter_control_settings *settings)
{
u32 hash_lut_size = 0 ;
int ret = 0 ;
u32 val;
if (!settings)
return -EINVAL;
/* Validate the input settings */
ret = i40e_validate_filter_settings(hw, settings);
if (ret)
return ret;
/* Read the PF Queue Filter control register */
val = i40e_read_rx_ctl(hw, I40E_PFQF_CTL_0);
/* Program required PE hash buckets for the PF */
val &= ~I40E_PFQF_CTL_0_PEHSIZE_MASK;
val |= FIELD_PREP(I40E_PFQF_CTL_0_PEHSIZE_MASK, settings->pe_filt_num);
/* Program required PE contexts for the PF */
val &= ~I40E_PFQF_CTL_0_PEDSIZE_MASK;
val |= FIELD_PREP(I40E_PFQF_CTL_0_PEDSIZE_MASK, settings->pe_cntx_num);
/* Program required FCoE hash buckets for the PF */
val &= ~I40E_PFQF_CTL_0_PFFCHSIZE_MASK;
val |= FIELD_PREP(I40E_PFQF_CTL_0_PFFCHSIZE_MASK,
settings->fcoe_filt_num);
/* Program required FCoE DDP contexts for the PF */
val &= ~I40E_PFQF_CTL_0_PFFCDSIZE_MASK;
val |= FIELD_PREP(I40E_PFQF_CTL_0_PFFCDSIZE_MASK,
settings->fcoe_cntx_num);
/* Program Hash LUT size for the PF */
val &= ~I40E_PFQF_CTL_0_HASHLUTSIZE_MASK;
if (settings->hash_lut_size == I40E_HASH_LUT_SIZE_512)
hash_lut_size = 1 ;
val |= FIELD_PREP(I40E_PFQF_CTL_0_HASHLUTSIZE_MASK, hash_lut_size);
/* Enable FDIR, Ethertype and MACVLAN filters for PF and VFs */
if (settings->enable_fdir)
val |= I40E_PFQF_CTL_0_FD_ENA_MASK;
if (settings->enable_ethtype)
val |= I40E_PFQF_CTL_0_ETYPE_ENA_MASK;
if (settings->enable_macvlan)
val |= I40E_PFQF_CTL_0_MACVLAN_ENA_MASK;
i40e_write_rx_ctl(hw, I40E_PFQF_CTL_0, val);
return 0 ;
}
/**
* i40e_aq_add_rem_control_packet_filter - Add or Remove Control Packet Filter
* @ hw : pointer to the hw struct
* @ mac_addr : MAC address to use in the filter
* @ ethtype : Ethertype to use in the filter
* @ flags : Flags that needs to be applied to the filter
* @ vsi_seid : seid of the control VSI
* @ queue : VSI queue number to send the packet to
* @ is_add : Add control packet filter if True else remove
* @ stats : Structure to hold information on control filter counts
* @ cmd_details : pointer to command details structure or NULL
*
* This command will Add or Remove control packet filter for a control VSI .
* In return it will update the total number of perfect filter count in
* the stats member .
**/
int i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
u8 *mac_addr, u16 ethtype, u16 flags,
u16 vsi_seid, u16 queue, bool is_add,
struct i40e_control_filter_stats *stats,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_add_remove_control_packet_filter_completion *resp;
struct i40e_aqc_add_remove_control_packet_filter *cmd;
struct libie_aq_desc desc;
int status;
if (vsi_seid == 0 )
return -EINVAL;
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
if (is_add) {
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_add_control_packet_filter);
cmd->queue = cpu_to_le16(queue);
} else {
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_remove_control_packet_filter);
}
if (mac_addr)
ether_addr_copy(cmd->mac, mac_addr);
cmd->etype = cpu_to_le16(ethtype);
cmd->flags = cpu_to_le16(flags);
cmd->seid = cpu_to_le16(vsi_seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status && stats) {
stats->mac_etype_used = le16_to_cpu(resp->mac_etype_used);
stats->etype_used = le16_to_cpu(resp->etype_used);
stats->mac_etype_free = le16_to_cpu(resp->mac_etype_free);
stats->etype_free = le16_to_cpu(resp->etype_free);
}
return status;
}
/**
* i40e_add_filter_to_drop_tx_flow_control_frames - filter to drop flow control
* @ hw : pointer to the hw struct
* @ seid : VSI seid to add ethertype filter from
**/
void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
u16 seid)
{
#define I40E_FLOW_CONTROL_ETHTYPE 0 x8808
u16 flag = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC |
I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP |
I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX;
u16 ethtype = I40E_FLOW_CONTROL_ETHTYPE;
int status;
status = i40e_aq_add_rem_control_packet_filter(hw, NULL, ethtype, flag,
seid, 0 , true , NULL,
NULL);
if (status)
hw_dbg(hw, "Ethtype Filter Add failed: Error pruning Tx flow control frames\n" );
}
/**
* i40e_aq_alternate_read
* @ hw : pointer to the hardware structure
* @ reg_addr0 : address of first dword to be read
* @ reg_val0 : pointer for data read from ' reg_addr0 '
* @ reg_addr1 : address of second dword to be read
* @ reg_val1 : pointer for data read from ' reg_addr1 '
*
* Read one or two dwords from alternate structure . Fields are indicated
* by ' reg_addr0 ' and ' reg_addr1 ' register numbers . If ' reg_val1 ' pointer
* is not passed then only register at ' reg_addr0 ' is read .
*
**/
static int i40e_aq_alternate_read(struct i40e_hw *hw,
u32 reg_addr0, u32 *reg_val0,
u32 reg_addr1, u32 *reg_val1)
{
struct i40e_aqc_alternate_write *cmd_resp;
struct libie_aq_desc desc;
int status;
if (!reg_val0)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_alternate_read);
cmd_resp = libie_aq_raw(&desc);
cmd_resp->address0 = cpu_to_le32(reg_addr0);
cmd_resp->address1 = cpu_to_le32(reg_addr1);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , NULL);
if (!status) {
*reg_val0 = le32_to_cpu(cmd_resp->data0);
if (reg_val1)
*reg_val1 = le32_to_cpu(cmd_resp->data1);
}
return status;
}
/**
* i40e_aq_suspend_port_tx
* @ hw : pointer to the hardware structure
* @ seid : port seid
* @ cmd_details : pointer to command details structure or NULL
*
* Suspend port ' s Tx traffic
**/
int i40e_aq_suspend_port_tx(struct i40e_hw *hw, u16 seid,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_tx_sched_ind *cmd;
struct libie_aq_desc desc;
int status;
cmd = libie_aq_raw(&desc);
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_suspend_port_tx);
cmd->vsi_seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_resume_port_tx
* @ hw : pointer to the hardware structure
* @ cmd_details : pointer to command details structure or NULL
*
* Resume port ' s Tx traffic
**/
int i40e_aq_resume_port_tx(struct i40e_hw *hw,
struct i40e_asq_cmd_details *cmd_details)
{
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_resume_port_tx);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_set_pci_config_data - store PCI bus info
* @ hw : pointer to hardware structure
* @ link_status : the link status word from PCI config space
*
* Stores the PCI bus info ( speed , width , type ) within the i40e_hw structure
**/
void i40e_set_pci_config_data(struct i40e_hw *hw, u16 link_status)
{
hw->bus.type = i40e_bus_type_pci_express;
switch (link_status & PCI_EXP_LNKSTA_NLW) {
case PCI_EXP_LNKSTA_NLW_X1:
hw->bus.width = i40e_bus_width_pcie_x1;
break ;
case PCI_EXP_LNKSTA_NLW_X2:
hw->bus.width = i40e_bus_width_pcie_x2;
break ;
case PCI_EXP_LNKSTA_NLW_X4:
hw->bus.width = i40e_bus_width_pcie_x4;
break ;
case PCI_EXP_LNKSTA_NLW_X8:
hw->bus.width = i40e_bus_width_pcie_x8;
break ;
default :
hw->bus.width = i40e_bus_width_unknown;
break ;
}
switch (link_status & PCI_EXP_LNKSTA_CLS) {
case PCI_EXP_LNKSTA_CLS_2_5GB:
hw->bus.speed = i40e_bus_speed_2500;
break ;
case PCI_EXP_LNKSTA_CLS_5_0GB:
hw->bus.speed = i40e_bus_speed_5000;
break ;
case PCI_EXP_LNKSTA_CLS_8_0GB:
hw->bus.speed = i40e_bus_speed_8000;
break ;
default :
hw->bus.speed = i40e_bus_speed_unknown;
break ;
}
}
/**
* i40e_aq_debug_dump
* @ hw : pointer to the hardware structure
* @ cluster_id : specific cluster to dump
* @ table_id : table id within cluster
* @ start_index : index of line in the block to read
* @ buff_size : dump buffer size
* @ buff : dump buffer
* @ ret_buff_size : actual buffer size returned
* @ ret_next_table : next block to read
* @ ret_next_index : next index to read
* @ cmd_details : pointer to command details structure or NULL
*
* Dump internal FW / HW data for debug purposes .
*
**/
int i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id,
u8 table_id, u32 start_index, u16 buff_size,
void *buff, u16 *ret_buff_size,
u8 *ret_next_table, u32 *ret_next_index,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_debug_dump_internals *resp;
struct i40e_aqc_debug_dump_internals *cmd;
struct libie_aq_desc desc;
int status;
if (buff_size == 0 || !buff)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_debug_dump_internals);
resp = libie_aq_raw(&desc);
cmd = libie_aq_raw(&desc);
/* Indirect Command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
cmd->cluster_id = cluster_id;
cmd->table_id = table_id;
cmd->idx = cpu_to_le32(start_index);
desc.datalen = cpu_to_le16(buff_size);
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
if (!status) {
if (ret_buff_size)
*ret_buff_size = le16_to_cpu(desc.datalen);
if (ret_next_table)
*ret_next_table = resp->table_id;
if (ret_next_index)
*ret_next_index = le32_to_cpu(resp->idx);
}
return status;
}
/**
* i40e_read_bw_from_alt_ram
* @ hw : pointer to the hardware structure
* @ max_bw : pointer for max_bw read
* @ min_bw : pointer for min_bw read
* @ min_valid : pointer for bool that is true if min_bw is a valid value
* @ max_valid : pointer for bool that is true if max_bw is a valid value
*
* Read bw from the alternate ram for the given pf
**/
int i40e_read_bw_from_alt_ram(struct i40e_hw *hw,
u32 *max_bw, u32 *min_bw,
bool *min_valid, bool *max_valid)
{
u32 max_bw_addr, min_bw_addr;
int status;
/* Calculate the address of the min/max bw registers */
max_bw_addr = I40E_ALT_STRUCT_FIRST_PF_OFFSET +
I40E_ALT_STRUCT_MAX_BW_OFFSET +
(I40E_ALT_STRUCT_DWORDS_PER_PF * hw->pf_id);
min_bw_addr = I40E_ALT_STRUCT_FIRST_PF_OFFSET +
I40E_ALT_STRUCT_MIN_BW_OFFSET +
(I40E_ALT_STRUCT_DWORDS_PER_PF * hw->pf_id);
/* Read the bandwidths from alt ram */
status = i40e_aq_alternate_read(hw, max_bw_addr, max_bw,
min_bw_addr, min_bw);
if (*min_bw & I40E_ALT_BW_VALID_MASK)
*min_valid = true ;
else
*min_valid = false ;
if (*max_bw & I40E_ALT_BW_VALID_MASK)
*max_valid = true ;
else
*max_valid = false ;
return status;
}
/**
* i40e_aq_configure_partition_bw
* @ hw : pointer to the hardware structure
* @ bw_data : Buffer holding valid pfs and bw limits
* @ cmd_details : pointer to command details
*
* Configure partitions guaranteed / max bw
**/
int
i40e_aq_configure_partition_bw(struct i40e_hw *hw,
struct i40e_aqc_configure_partition_bw_data *bw_data,
struct i40e_asq_cmd_details *cmd_details)
{
u16 bwd_size = sizeof (*bw_data);
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_configure_partition_bw);
/* Indirect command */
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_RD);
if (bwd_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(bwd_size);
status = i40e_asq_send_command(hw, &desc, bw_data, bwd_size,
cmd_details);
return status;
}
/**
* i40e_read_phy_register_clause22
* @ hw : pointer to the HW structure
* @ reg : register address in the page
* @ phy_addr : PHY address on MDIO interface
* @ value : PHY register value
*
* Reads specified PHY register value
**/
int i40e_read_phy_register_clause22(struct i40e_hw *hw,
u16 reg, u8 phy_addr, u16 *value)
{
u8 port_num = (u8)hw->func_caps.mdio_port_num;
int status = -EIO;
u32 command = 0 ;
u16 retry = 1000 ;
command = (reg << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE22_OPCODE_READ_MASK) |
(I40E_MDIO_CLAUSE22_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
udelay(10 );
retry--;
} while (retry);
if (status) {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't write command to external PHY.\n" );
} else {
command = rd32(hw, I40E_GLGEN_MSRWD(port_num));
*value = FIELD_GET(I40E_GLGEN_MSRWD_MDIRDDATA_MASK, command);
}
return status;
}
/**
* i40e_write_phy_register_clause22
* @ hw : pointer to the HW structure
* @ reg : register address in the page
* @ phy_addr : PHY address on MDIO interface
* @ value : PHY register value
*
* Writes specified PHY register value
**/
int i40e_write_phy_register_clause22(struct i40e_hw *hw,
u16 reg, u8 phy_addr, u16 value)
{
u8 port_num = (u8)hw->func_caps.mdio_port_num;
int status = -EIO;
u32 command = 0 ;
u16 retry = 1000 ;
command = value << I40E_GLGEN_MSRWD_MDIWRDATA_SHIFT;
wr32(hw, I40E_GLGEN_MSRWD(port_num), command);
command = (reg << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE22_OPCODE_WRITE_MASK) |
(I40E_MDIO_CLAUSE22_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
udelay(10 );
retry--;
} while (retry);
return status;
}
/**
* i40e_read_phy_register_clause45
* @ hw : pointer to the HW structure
* @ page : registers page number
* @ reg : register address in the page
* @ phy_addr : PHY address on MDIO interface
* @ value : PHY register value
*
* Reads specified PHY register value
**/
int i40e_read_phy_register_clause45(struct i40e_hw *hw,
u8 page, u16 reg, u8 phy_addr, u16 *value)
{
u8 port_num = hw->func_caps.mdio_port_num;
int status = -EIO;
u32 command = 0 ;
u16 retry = 1000 ;
command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) |
(page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE45_OPCODE_ADDRESS_MASK) |
(I40E_MDIO_CLAUSE45_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
usleep_range(10 , 20 );
retry--;
} while (retry);
if (status) {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't write command to external PHY.\n" );
goto phy_read_end;
}
command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE45_OPCODE_READ_MASK) |
(I40E_MDIO_CLAUSE45_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
status = -EIO;
retry = 1000 ;
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
usleep_range(10 , 20 );
retry--;
} while (retry);
if (!status) {
command = rd32(hw, I40E_GLGEN_MSRWD(port_num));
*value = FIELD_GET(I40E_GLGEN_MSRWD_MDIRDDATA_MASK, command);
} else {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't read register value from external PHY.\n" );
}
phy_read_end:
return status;
}
/**
* i40e_write_phy_register_clause45
* @ hw : pointer to the HW structure
* @ page : registers page number
* @ reg : register address in the page
* @ phy_addr : PHY address on MDIO interface
* @ value : PHY register value
*
* Writes value to specified PHY register
**/
int i40e_write_phy_register_clause45(struct i40e_hw *hw,
u8 page, u16 reg, u8 phy_addr, u16 value)
{
u8 port_num = hw->func_caps.mdio_port_num;
int status = -EIO;
u16 retry = 1000 ;
u32 command = 0 ;
command = (reg << I40E_GLGEN_MSCA_MDIADD_SHIFT) |
(page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE45_OPCODE_ADDRESS_MASK) |
(I40E_MDIO_CLAUSE45_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
usleep_range(10 , 20 );
retry--;
} while (retry);
if (status) {
i40e_debug(hw, I40E_DEBUG_PHY,
"PHY: Can't write command to external PHY.\n" );
goto phy_write_end;
}
command = value << I40E_GLGEN_MSRWD_MDIWRDATA_SHIFT;
wr32(hw, I40E_GLGEN_MSRWD(port_num), command);
command = (page << I40E_GLGEN_MSCA_DEVADD_SHIFT) |
(phy_addr << I40E_GLGEN_MSCA_PHYADD_SHIFT) |
(I40E_MDIO_CLAUSE45_OPCODE_WRITE_MASK) |
(I40E_MDIO_CLAUSE45_STCODE_MASK) |
(I40E_GLGEN_MSCA_MDICMD_MASK) |
(I40E_GLGEN_MSCA_MDIINPROGEN_MASK);
status = -EIO;
retry = 1000 ;
wr32(hw, I40E_GLGEN_MSCA(port_num), command);
do {
command = rd32(hw, I40E_GLGEN_MSCA(port_num));
if (!(command & I40E_GLGEN_MSCA_MDICMD_MASK)) {
status = 0 ;
break ;
}
usleep_range(10 , 20 );
retry--;
} while (retry);
phy_write_end:
return status;
}
/**
* i40e_get_phy_address
* @ hw : pointer to the HW structure
* @ dev_num : PHY port num that address we want
*
* Gets PHY address for current port
**/
u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num)
{
u8 port_num = hw->func_caps.mdio_port_num;
u32 reg_val = rd32(hw, I40E_GLGEN_MDIO_I2C_SEL(port_num));
return (u8)(reg_val >> ((dev_num + 1 ) * 5 )) & 0 x1f;
}
/**
* i40e_led_get_reg - read LED register
* @ hw : pointer to the HW structure
* @ led_addr : LED register address
* @ reg_val : read register value
**/
static int i40e_led_get_reg(struct i40e_hw *hw, u16 led_addr,
u32 *reg_val)
{
u8 phy_addr = 0 ;
u8 port_num;
int status;
u32 i;
*reg_val = 0 ;
if (test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps)) {
status =
i40e_aq_get_phy_register(hw,
I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
I40E_PHY_COM_REG_PAGE, true ,
I40E_PHY_LED_PROV_REG_1,
reg_val, NULL);
} else {
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
status = i40e_read_phy_register_clause45(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr,
(u16 *)reg_val);
}
return status;
}
/**
* i40e_led_set_reg - write LED register
* @ hw : pointer to the HW structure
* @ led_addr : LED register address
* @ reg_val : register value to write
**/
static int i40e_led_set_reg(struct i40e_hw *hw, u16 led_addr,
u32 reg_val)
{
u8 phy_addr = 0 ;
u8 port_num;
int status;
u32 i;
if (test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps)) {
status =
i40e_aq_set_phy_register(hw,
I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
I40E_PHY_COM_REG_PAGE, true ,
I40E_PHY_LED_PROV_REG_1,
reg_val, NULL);
} else {
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
status = i40e_write_phy_register_clause45(hw,
I40E_PHY_COM_REG_PAGE,
led_addr, phy_addr,
(u16)reg_val);
}
return status;
}
/**
* i40e_led_get_phy - return current on / off mode
* @ hw : pointer to the hw struct
* @ led_addr : address of led register to use
* @ val : original value of register to use
*
**/
int i40e_led_get_phy(struct i40e_hw *hw, u16 *led_addr,
u16 *val)
{
u16 gpio_led_port;
u8 phy_addr = 0 ;
u32 reg_val_aq;
int status = 0 ;
u16 temp_addr;
u16 reg_val;
u8 port_num;
u32 i;
if (test_bit(I40E_HW_CAP_AQ_PHY_ACCESS, hw->caps)) {
status =
i40e_aq_get_phy_register(hw,
I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
I40E_PHY_COM_REG_PAGE, true ,
I40E_PHY_LED_PROV_REG_1,
®_val_aq, NULL);
if (status == 0 )
*val = (u16)reg_val_aq;
return status;
}
temp_addr = I40E_PHY_LED_PROV_REG_1;
i = rd32(hw, I40E_PFGEN_PORTNUM);
port_num = (u8)(i & I40E_PFGEN_PORTNUM_PORT_NUM_MASK);
phy_addr = i40e_get_phy_address(hw, port_num);
for (gpio_led_port = 0 ; gpio_led_port < 3 ; gpio_led_port++,
temp_addr++) {
status = i40e_read_phy_register_clause45(hw,
I40E_PHY_COM_REG_PAGE,
temp_addr, phy_addr,
®_val);
if (status)
return status;
*val = reg_val;
if (reg_val & I40E_PHY_LED_LINK_MODE_MASK) {
*led_addr = temp_addr;
break ;
}
}
return status;
}
/**
* i40e_led_set_phy
* @ hw : pointer to the HW structure
* @ on : true or false
* @ led_addr : address of led register to use
* @ mode : original val plus bit for set or ignore
*
* Set led ' s on or off when controlled by the PHY
*
**/
int i40e_led_set_phy(struct i40e_hw *hw, bool on,
u16 led_addr, u32 mode)
{
u32 led_ctl = 0 ;
u32 led_reg = 0 ;
int status = 0 ;
status = i40e_led_get_reg(hw, led_addr, &led_reg);
if (status)
return status;
led_ctl = led_reg;
if (led_reg & I40E_PHY_LED_LINK_MODE_MASK) {
led_reg = 0 ;
status = i40e_led_set_reg(hw, led_addr, led_reg);
if (status)
return status;
}
status = i40e_led_get_reg(hw, led_addr, &led_reg);
if (status)
goto restore_config;
if (on)
led_reg = I40E_PHY_LED_MANUAL_ON;
else
led_reg = 0 ;
status = i40e_led_set_reg(hw, led_addr, led_reg);
if (status)
goto restore_config;
if (mode & I40E_PHY_LED_MODE_ORIG) {
led_ctl = (mode & I40E_PHY_LED_MODE_MASK);
status = i40e_led_set_reg(hw, led_addr, led_ctl);
}
return status;
restore_config:
status = i40e_led_set_reg(hw, led_addr, led_ctl);
return status;
}
/**
* i40e_aq_rx_ctl_read_register - use FW to read from an Rx control register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
* @ reg_val : ptr to register value
* @ cmd_details : pointer to command details structure or NULL
*
* Use the firmware to read the Rx control register ,
* especially useful if the Rx unit is under heavy pressure
**/
int i40e_aq_rx_ctl_read_register(struct i40e_hw *hw,
u32 reg_addr, u32 *reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_rx_ctl_reg_read_write *cmd_resp;
struct libie_aq_desc desc;
int status;
if (!reg_val)
return -EINVAL;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_read);
cmd_resp = libie_aq_raw(&desc);
cmd_resp->address = cpu_to_le32(reg_addr);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (status == 0 )
*reg_val = le32_to_cpu(cmd_resp->value);
return status;
}
/**
* i40e_read_rx_ctl - read from an Rx control register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
**/
u32 i40e_read_rx_ctl(struct i40e_hw *hw, u32 reg_addr)
{
bool use_register = false ;
int status = 0 ;
int retry = 5 ;
u32 val = 0 ;
if (i40e_is_aq_api_ver_lt(hw, 1 , 5 ) || hw->mac.type == I40E_MAC_X722)
use_register = true ;
if (!use_register) {
do_retry:
status = i40e_aq_rx_ctl_read_register(hw, reg_addr, &val, NULL);
if (hw->aq.asq_last_status == LIBIE_AQ_RC_EAGAIN && retry) {
usleep_range(1000 , 2000 );
retry--;
goto do_retry;
}
}
/* if the AQ access failed, try the old-fashioned way */
if (status || use_register)
val = rd32(hw, reg_addr);
return val;
}
/**
* i40e_aq_rx_ctl_write_register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
* @ reg_val : register value
* @ cmd_details : pointer to command details structure or NULL
*
* Use the firmware to write to an Rx control register ,
* especially useful if the Rx unit is under heavy pressure
**/
int i40e_aq_rx_ctl_write_register(struct i40e_hw *hw,
u32 reg_addr, u32 reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_rx_ctl_reg_read_write *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_write);
cmd = libie_aq_raw(&desc);
cmd->address = cpu_to_le32(reg_addr);
cmd->value = cpu_to_le32(reg_val);
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_write_rx_ctl - write to an Rx control register
* @ hw : pointer to the hw struct
* @ reg_addr : register address
* @ reg_val : register value
**/
void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val)
{
bool use_register = false ;
int status = 0 ;
int retry = 5 ;
if (i40e_is_aq_api_ver_lt(hw, 1 , 5 ) || hw->mac.type == I40E_MAC_X722)
use_register = true ;
if (!use_register) {
do_retry:
status = i40e_aq_rx_ctl_write_register(hw, reg_addr,
reg_val, NULL);
if (hw->aq.asq_last_status == LIBIE_AQ_RC_EAGAIN && retry) {
usleep_range(1000 , 2000 );
retry--;
goto do_retry;
}
}
/* if the AQ access failed, try the old-fashioned way */
if (status || use_register)
wr32(hw, reg_addr, reg_val);
}
/**
* i40e_mdio_if_number_selection - MDIO I / F number selection
* @ hw : pointer to the hw struct
* @ set_mdio : use MDIO I / F number specified by mdio_num
* @ mdio_num : MDIO I / F number
* @ cmd : pointer to PHY Register command structure
**/
static void i40e_mdio_if_number_selection(struct i40e_hw *hw, bool set_mdio,
u8 mdio_num,
struct i40e_aqc_phy_register_access *cmd)
{
if (!set_mdio ||
cmd->phy_interface != I40E_AQ_PHY_REG_ACCESS_EXTERNAL)
return ;
if (test_bit(I40E_HW_CAP_AQ_PHY_ACCESS_EXTENDED, hw->caps)) {
cmd->cmd_flags |=
I40E_AQ_PHY_REG_ACCESS_SET_MDIO_IF_NUMBER |
FIELD_PREP(I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_MASK,
mdio_num);
} else {
i40e_debug(hw, I40E_DEBUG_PHY, "MDIO I/F number selection not supported by current FW version.\n" );
}
}
/**
* i40e_aq_set_phy_register_ext
* @ hw : pointer to the hw struct
* @ phy_select : select which phy should be accessed
* @ dev_addr : PHY device address
* @ page_change : flag to indicate if phy page should be updated
* @ set_mdio : use MDIO I / F number specified by mdio_num
* @ mdio_num : MDIO I / F number
* @ reg_addr : PHY register address
* @ reg_val : new register value
* @ cmd_details : pointer to command details structure or NULL
*
* Write the external PHY register .
* NOTE : In common cases MDIO I / F number should not be changed , thats why you
* may use simple wrapper i40e_aq_set_phy_register .
**/
int i40e_aq_set_phy_register_ext(struct i40e_hw *hw,
u8 phy_select, u8 dev_addr, bool page_change,
bool set_mdio, u8 mdio_num,
u32 reg_addr, u32 reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_phy_register_access *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_phy_register);
cmd = libie_aq_raw(&desc);
cmd->phy_interface = phy_select;
cmd->dev_address = dev_addr;
cmd->reg_address = cpu_to_le32(reg_addr);
cmd->reg_value = cpu_to_le32(reg_val);
i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd);
if (!page_change)
cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
return status;
}
/**
* i40e_aq_get_phy_register_ext
* @ hw : pointer to the hw struct
* @ phy_select : select which phy should be accessed
* @ dev_addr : PHY device address
* @ page_change : flag to indicate if phy page should be updated
* @ set_mdio : use MDIO I / F number specified by mdio_num
* @ mdio_num : MDIO I / F number
* @ reg_addr : PHY register address
* @ reg_val : read register value
* @ cmd_details : pointer to command details structure or NULL
*
* Read the external PHY register .
* NOTE : In common cases MDIO I / F number should not be changed , thats why you
* may use simple wrapper i40e_aq_get_phy_register .
**/
int i40e_aq_get_phy_register_ext(struct i40e_hw *hw,
u8 phy_select, u8 dev_addr, bool page_change,
bool set_mdio, u8 mdio_num,
u32 reg_addr, u32 *reg_val,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_phy_register_access *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_phy_register);
cmd = libie_aq_raw(&desc);
cmd->phy_interface = phy_select;
cmd->dev_address = dev_addr;
cmd->reg_address = cpu_to_le32(reg_addr);
i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd);
if (!page_change)
cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE;
status = i40e_asq_send_command(hw, &desc, NULL, 0 , cmd_details);
if (!status)
*reg_val = le32_to_cpu(cmd->reg_value);
return status;
}
/**
* i40e_aq_write_ddp - Write dynamic device personalization ( ddp )
* @ hw : pointer to the hw struct
* @ buff : command buffer ( size in bytes = buff_size )
* @ buff_size : buffer size in bytes
* @ track_id : package tracking id
* @ error_offset : returns error offset
* @ error_info : returns error information
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_write_ddp(struct i40e_hw *hw, void *buff,
u16 buff_size, u32 track_id,
u32 *error_offset, u32 *error_info,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_write_personalization_profile *cmd;
struct i40e_aqc_write_ddp_resp *resp;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_write_personalization_profile);
cmd = libie_aq_raw(&desc);
desc.flags |= cpu_to_le16(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(buff_size);
cmd->profile_track_id = cpu_to_le32(track_id);
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
if (!status) {
resp = libie_aq_raw(&desc);
if (error_offset)
*error_offset = le32_to_cpu(resp->error_offset);
if (error_info)
*error_info = le32_to_cpu(resp->error_info);
}
return status;
}
/**
* i40e_aq_get_ddp_list - Read dynamic device personalization ( ddp )
* @ hw : pointer to the hw struct
* @ buff : command buffer ( size in bytes = buff_size )
* @ buff_size : buffer size in bytes
* @ flags : AdminQ command flags
* @ cmd_details : pointer to command details structure or NULL
**/
int i40e_aq_get_ddp_list(struct i40e_hw *hw, void *buff,
u16 buff_size, u8 flags,
struct i40e_asq_cmd_details *cmd_details)
{
struct i40e_aqc_get_applied_profiles *cmd;
struct libie_aq_desc desc;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_get_personalization_profile_list);
cmd = libie_aq_raw(&desc);
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_BUF);
if (buff_size > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(buff_size);
cmd->flags = flags;
status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details);
return status;
}
/**
* i40e_find_segment_in_package
* @ segment_type : the segment type to search for ( i . e . , SEGMENT_TYPE_I40E )
* @ pkg_hdr : pointer to the package header to be searched
*
* This function searches a package file for a particular segment type . On
* success it returns a pointer to the segment header , otherwise it will
* return NULL .
**/
struct i40e_generic_seg_header *
i40e_find_segment_in_package(u32 segment_type,
struct i40e_package_header *pkg_hdr)
{
struct i40e_generic_seg_header *segment;
u32 i;
/* Search all package segments for the requested segment type */
for (i = 0 ; i < pkg_hdr->segment_count; i++) {
segment =
(struct i40e_generic_seg_header *)((u8 *)pkg_hdr +
pkg_hdr->segment_offset[i]);
if (segment->type == segment_type)
return segment;
}
return NULL;
}
/* Get section table in profile */
#define I40E_SECTION_TABLE(profile, sec_tbl) \
do { \
struct i40e_profile_segment *p = (profile); \
u32 count; \
u32 *nvm; \
count = p->device_table_count; \
nvm = (u32 *)&p->device_table[count]; \
sec_tbl = (struct i40e_section_table *)&nvm[nvm[0 ] + 1 ]; \
} while (0 )
/* Get section header in profile */
#define I40E_SECTION_HEADER(profile, offset) \
(struct i40e_profile_section_header *)((u8 *)(profile) + (offset))
/**
* i40e_ddp_exec_aq_section - Execute generic AQ for DDP
* @ hw : pointer to the hw struct
* @ aq : command buffer containing all data to execute AQ
**/
static int i40e_ddp_exec_aq_section(struct i40e_hw *hw,
struct i40e_profile_aq_section *aq)
{
struct libie_aq_desc desc;
u8 *msg = NULL;
u16 msglen;
int status;
i40e_fill_default_direct_cmd_desc(&desc, aq->opcode);
desc.flags |= cpu_to_le16(aq->flags);
memcpy(desc.params.raw, aq->param, sizeof (desc.params.raw));
msglen = aq->datalen;
if (msglen) {
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF |
LIBIE_AQ_FLAG_RD));
if (msglen > I40E_AQ_LARGE_BUF)
desc.flags |= cpu_to_le16((u16)LIBIE_AQ_FLAG_LB);
desc.datalen = cpu_to_le16(msglen);
msg = &aq->data[0 ];
}
status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"unable to exec DDP AQ opcode %u, error %d\n" ,
aq->opcode, status);
return status;
}
/* copy returned desc to aq_buf */
memcpy(aq->param, desc.params.raw, sizeof (desc.params.raw));
return 0 ;
}
/**
* i40e_validate_profile
* @ hw : pointer to the hardware structure
* @ profile : pointer to the profile segment of the package to be validated
* @ track_id : package tracking id
* @ rollback : flag if the profile is for rollback .
*
* Validates supported devices and profile ' s sections .
*/
static int
i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
u32 track_id, bool rollback)
{
struct i40e_profile_section_header *sec = NULL;
struct i40e_section_table *sec_tbl;
u32 vendor_dev_id;
int status = 0 ;
u32 dev_cnt;
u32 sec_off;
u32 i;
if (track_id == I40E_DDP_TRACKID_INVALID) {
i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n" );
return -EOPNOTSUPP;
}
dev_cnt = profile->device_table_count;
for (i = 0 ; i < dev_cnt; i++) {
vendor_dev_id = profile->device_table[i].vendor_dev_id;
if ((vendor_dev_id >> 16 ) == PCI_VENDOR_ID_INTEL &&
hw->device_id == (vendor_dev_id & 0 xFFFF))
break ;
}
if (dev_cnt && i == dev_cnt) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Device doesn't support DDP\n" );
return -ENODEV;
}
I40E_SECTION_TABLE(profile, sec_tbl);
/* Validate sections types */
for (i = 0 ; i < sec_tbl->section_count; i++) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
if (rollback) {
if (sec->section.type == SECTION_TYPE_MMIO ||
sec->section.type == SECTION_TYPE_AQ ||
sec->section.type == SECTION_TYPE_RB_AQ) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Not a roll-back package\n" );
return -EOPNOTSUPP;
}
} else {
if (sec->section.type == SECTION_TYPE_RB_AQ ||
sec->section.type == SECTION_TYPE_RB_MMIO) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Not an original package\n" );
return -EOPNOTSUPP;
}
}
}
return status;
}
/**
* i40e_write_profile
* @ hw : pointer to the hardware structure
* @ profile : pointer to the profile segment of the package to be downloaded
* @ track_id : package tracking id
*
* Handles the download of a complete package .
*/
int
i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
u32 track_id)
{
struct i40e_profile_section_header *sec = NULL;
struct i40e_profile_aq_section *ddp_aq;
struct i40e_section_table *sec_tbl;
u32 offset = 0 , info = 0 ;
u32 section_size = 0 ;
int status = 0 ;
u32 sec_off;
u32 i;
status = i40e_validate_profile(hw, profile, track_id, false );
if (status)
return status;
I40E_SECTION_TABLE(profile, sec_tbl);
for (i = 0 ; i < sec_tbl->section_count; i++) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
/* Process generic admin command */
if (sec->section.type == SECTION_TYPE_AQ) {
ddp_aq = (struct i40e_profile_aq_section *)&sec[1 ];
status = i40e_ddp_exec_aq_section(hw, ddp_aq);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to execute aq: section %d, opcode %u\n" ,
i, ddp_aq->opcode);
break ;
}
sec->section.type = SECTION_TYPE_RB_AQ;
}
/* Skip any non-mmio sections */
if (sec->section.type != SECTION_TYPE_MMIO)
continue ;
section_size = sec->section.size +
sizeof (struct i40e_profile_section_header);
/* Write MMIO section */
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
track_id, &offset, &info, NULL);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to write profile: section %d, offset %d, info %d\n" ,
i, offset, info);
break ;
}
}
return status;
}
/**
* i40e_rollback_profile
* @ hw : pointer to the hardware structure
* @ profile : pointer to the profile segment of the package to be removed
* @ track_id : package tracking id
*
* Rolls back previously loaded package .
*/
int
i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile,
u32 track_id)
{
struct i40e_profile_section_header *sec = NULL;
struct i40e_section_table *sec_tbl;
u32 offset = 0 , info = 0 ;
u32 section_size = 0 ;
int status = 0 ;
u32 sec_off;
int i;
status = i40e_validate_profile(hw, profile, track_id, true );
if (status)
return status;
I40E_SECTION_TABLE(profile, sec_tbl);
/* For rollback write sections in reverse */
for (i = sec_tbl->section_count - 1 ; i >= 0 ; i--) {
sec_off = sec_tbl->section_offset[i];
sec = I40E_SECTION_HEADER(profile, sec_off);
/* Skip any non-rollback sections */
if (sec->section.type != SECTION_TYPE_RB_MMIO)
continue ;
section_size = sec->section.size +
sizeof (struct i40e_profile_section_header);
/* Write roll-back MMIO section */
status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size,
track_id, &offset, &info, NULL);
if (status) {
i40e_debug(hw, I40E_DEBUG_PACKAGE,
"Failed to write profile: section %d, offset %d, info %d\n" ,
i, offset, info);
break ;
}
}
return status;
}
/**
* i40e_aq_add_cloud_filters
* @ hw : pointer to the hardware structure
* @ seid : VSI seid to add cloud filters from
* @ filters : Buffer which contains the filters to be added
* @ filter_count : number of filters contained in the buffer
*
* Set the cloud filters for a given VSI . The contents of the
* i40e_aqc_cloud_filters_element_data are filled in by the caller
* of the function .
*
**/
int
i40e_aq_add_cloud_filters(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_data *filters,
u8 filter_count)
{
struct i40e_aqc_add_remove_cloud_filters *cmd;
struct libie_aq_desc desc;
u16 buff_len;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_add_cloud_filters);
cmd = libie_aq_raw(&desc);
buff_len = filter_count * sizeof (*filters);
desc.datalen = cpu_to_le16(buff_len);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
cmd->num_filters = filter_count;
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
return status;
}
/**
* i40e_aq_add_cloud_filters_bb
* @ hw : pointer to the hardware structure
* @ seid : VSI seid to add cloud filters from
* @ filters : Buffer which contains the filters in big buffer to be added
* @ filter_count : number of filters contained in the buffer
*
* Set the big buffer cloud filters for a given VSI . The contents of the
* i40e_aqc_cloud_filters_element_bb are filled in by the caller of the
* function .
*
**/
int
i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count)
{
struct i40e_aqc_add_remove_cloud_filters *cmd;
struct libie_aq_desc desc;
u16 buff_len;
int status;
int i;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_add_cloud_filters);
cmd = libie_aq_raw(&desc);
buff_len = filter_count * sizeof (*filters);
desc.datalen = cpu_to_le16(buff_len);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
cmd->num_filters = filter_count;
cmd->seid = cpu_to_le16(seid);
cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB;
for (i = 0 ; i < filter_count; i++) {
u16 tnl_type;
u32 ti;
tnl_type = le16_get_bits(filters[i].element.flags,
I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK);
/* Due to hardware eccentricities, the VNI for Geneve is shifted
* one more byte further than normally used for Tenant ID in
* other tunnel types .
*/
if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
ti = le32_to_cpu(filters[i].element.tenant_id);
filters[i].element.tenant_id = cpu_to_le32(ti << 8 );
}
}
status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
return status;
}
/**
* i40e_aq_rem_cloud_filters
* @ hw : pointer to the hardware structure
* @ seid : VSI seid to remove cloud filters from
* @ filters : Buffer which contains the filters to be removed
* @ filter_count : number of filters contained in the buffer
*
* Remove the cloud filters for a given VSI . The contents of the
* i40e_aqc_cloud_filters_element_data are filled in by the caller
* of the function .
*
**/
int
i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_data *filters,
u8 filter_count)
{
struct i40e_aqc_add_remove_cloud_filters *cmd;
struct libie_aq_desc desc;
u16 buff_len;
int status;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_remove_cloud_filters);
cmd = libie_aq_raw(&desc);
buff_len = filter_count * sizeof (*filters);
desc.datalen = cpu_to_le16(buff_len);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
cmd->num_filters = filter_count;
cmd->seid = cpu_to_le16(seid);
status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
return status;
}
/**
* i40e_aq_rem_cloud_filters_bb
* @ hw : pointer to the hardware structure
* @ seid : VSI seid to remove cloud filters from
* @ filters : Buffer which contains the filters in big buffer to be removed
* @ filter_count : number of filters contained in the buffer
*
* Remove the big buffer cloud filters for a given VSI . The contents of the
* i40e_aqc_cloud_filters_element_bb are filled in by the caller of the
* function .
*
**/
int
i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
struct i40e_aqc_cloud_filters_element_bb *filters,
u8 filter_count)
{
struct i40e_aqc_add_remove_cloud_filters *cmd;
struct libie_aq_desc desc;
u16 buff_len;
int status;
int i;
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_remove_cloud_filters);
cmd = libie_aq_raw(&desc);
buff_len = filter_count * sizeof (*filters);
desc.datalen = cpu_to_le16(buff_len);
desc.flags |= cpu_to_le16((u16)(LIBIE_AQ_FLAG_BUF | LIBIE_AQ_FLAG_RD));
cmd->num_filters = filter_count;
cmd->seid = cpu_to_le16(seid);
cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB;
for (i = 0 ; i < filter_count; i++) {
u16 tnl_type;
u32 ti;
tnl_type = le16_get_bits(filters[i].element.flags,
I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK);
/* Due to hardware eccentricities, the VNI for Geneve is shifted
* one more byte further than normally used for Tenant ID in
* other tunnel types .
*/
if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
ti = le32_to_cpu(filters[i].element.tenant_id);
filters[i].element.tenant_id = cpu_to_le32(ti << 8 );
}
}
status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
return status;
}
Messung V0.5 in Prozent C=96 H=94 G=94
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.195Bemerkung:
¤
*Bot Zugriff