[Intel-wired-lan] [next PATCH S78-V4 03/12] i40e: Add support for 'ethtool -m'
Shannon Nelson
shannon.nelson at oracle.com
Mon Aug 7 17:19:05 UTC 2017
On 8/4/2017 6:52 AM, Alice Michael wrote:
> From: Filip Sadowski <filip.sadowski at intel.com>
>
> This patch adds support for 'ethtool -m' command which displays
> information about (Q)SFP+ module plugged into NIC's cage.
>
> Signed-off-by: Filip Sadowski <filip.sadowski at intel.com>
> ---
> drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 18 +++
> drivers/net/ethernet/intel/i40e/i40e_common.c | 69 +++++++++
> drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 154 +++++++++++++++++++++
> drivers/net/ethernet/intel/i40e/i40e_prototype.h | 9 ++
> drivers/net/ethernet/intel/i40e/i40e_type.h | 12 ++
> .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 18 +++
> drivers/net/ethernet/intel/i40evf/i40e_common.c | 69 +++++++++
> drivers/net/ethernet/intel/i40evf/i40e_prototype.h | 9 ++
> drivers/net/ethernet/intel/i40evf/i40e_type.h | 12 ++
> 9 files changed, 370 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> index 5d0291c..8fcbbd8 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
> @@ -244,6 +244,8 @@ enum i40e_admin_queue_opc {
> i40e_aqc_opc_set_phy_debug = 0x0622,
> i40e_aqc_opc_upload_ext_phy_fm = 0x0625,
> i40e_aqc_opc_run_phy_activity = 0x0626,
> + i40e_aqc_opc_set_phy_register = 0x0628,
> + i40e_aqc_opc_get_phy_register = 0x0629,
>
> /* NVM commands */
> i40e_aqc_opc_nvm_read = 0x0701,
> @@ -2053,6 +2055,22 @@ struct i40e_aqc_run_phy_activity {
>
> I40E_CHECK_CMD_LENGTH(i40e_aqc_run_phy_activity);
>
> +/* Set PHY Register command (0x0628) */
> +/* Get PHY Register command (0x0629) */
> +struct i40e_aqc_phy_register_access {
> + u8 phy_interface;
> +#define I40E_AQ_PHY_REG_ACCESS_INTERNAL 0
> +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL 1
> +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE 2
> + u8 dev_address;
> + u8 reserved1[2];
> + u32 reg_address;
> + u32 reg_value;
> + u8 reserved2[4];
> +};
> +
> +I40E_CHECK_CMD_LENGTH(i40e_aqc_phy_register_access);
> +
> /* NVM Read command (indirect 0x0701)
> * NVM Erase commands (direct 0x0702)
> * NVM Update commands (indirect 0x0703)
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
> index c0aeda7..6b7712c 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
> @@ -5143,6 +5143,75 @@ void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val)
> }
>
> /**
> + * i40e_aq_set_phy_register
> + * @hw: pointer to the hw struct
> + * @phy_select: select which phy should be accessed
> + * @dev_addr: PHY device address
> + * @reg_addr: PHY register address
> + * @reg_val: new register value
> + * @cmd_details: pointer to command details structure or NULL
> + *
> + * Write the external PHY register.
> + **/
> +i40e_status i40e_aq_set_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 reg_val,
> + struct i40e_asq_cmd_details *cmd_details)
> +{
> + struct i40e_aq_desc desc;
> + struct i40e_aqc_phy_register_access *cmd =
> + (struct i40e_aqc_phy_register_access *)&desc.params.raw;
> + i40e_status status;
> +
> + i40e_fill_default_direct_cmd_desc(&desc,
> + i40e_aqc_opc_set_phy_register);
> +
> + cmd->phy_interface = phy_select;
> + cmd->dev_address = dev_addr;
> + cmd->reg_address = reg_addr;
> + cmd->reg_value = reg_val;
Byteswapping?
> +
> + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
> +
> + return status;
> +}
> +
> +/**
> + * i40e_aq_get_phy_register
> + * @hw: pointer to the hw struct
> + * @phy_select: select which phy should be accessed
> + * @dev_addr: PHY device address
> + * @reg_addr: PHY register address
> + * @reg_val: read register value
> + * @cmd_details: pointer to command details structure or NULL
> + *
> + * Read the external PHY register.
> + **/
> +i40e_status i40e_aq_get_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 *reg_val,
> + struct i40e_asq_cmd_details *cmd_details)
> +{
> + struct i40e_aq_desc desc;
> + struct i40e_aqc_phy_register_access *cmd =
> + (struct i40e_aqc_phy_register_access *)&desc.params.raw;
> + i40e_status status;
> +
> + i40e_fill_default_direct_cmd_desc(&desc,
> + i40e_aqc_opc_get_phy_register);
> +
> + cmd->phy_interface = phy_select;
> + cmd->dev_address = dev_addr;
> + cmd->reg_address = reg_addr;
Byteswapping?
> +
> + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
> + if (!status)
> + *reg_val = cmd->reg_value;
Byteswapping?
> +
> + return status;
> +}
> +
> +/**
> * i40e_aq_write_ppp - Write pipeline personalization profile (ppp)
> * @hw: pointer to the hw struct
> * @buff: command buffer (size in bytes = buff_size)
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> index 326fc18..72910c5 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> @@ -4202,6 +4202,158 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
> return 0;
> }
>
> +/**
> + * i40e_get_module_info - get (Q)SFP+ module type info
> + * @netdev: network interface device structure
> + * @modinfo: module EEPROM size and layout information structure
> + **/
> +static int i40e_get_module_info(struct net_device *netdev,
> + struct ethtool_modinfo *modinfo)
> +{
> + i40e_status status;
> + struct i40e_netdev_priv *np = netdev_priv(netdev);
> + struct i40e_vsi *vsi = np->vsi;
> + struct i40e_pf *pf = vsi->back;
> + struct i40e_hw *hw = &pf->hw;
> + u32 sff8472_comp = 0;
> + u32 sff8472_swap = 0;
> + u32 sff8636_rev = 0;
> + u32 type = 0;
> +
> + /* Check if firmware supports reading module EEPROM. */
> + if (!(hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_CAPABLE)) {
> + netdev_err(vsi->netdev, "Module EEPROM memory read not supported. Please update the NVM image.\n");
> + return -EINVAL;
> + }
> +
> + status = i40e_update_link_info(hw);
> + if (status)
> + return -EIO;
> +
> + if (hw->phy.link_info.phy_type == I40E_PHY_TYPE_EMPTY) {
> + netdev_err(vsi->netdev, "Cannot read module EEPROM memory. No module connected.\n");
> + return -EINVAL;
> + }
> +
> + type = hw->phy.link_info.module_type[0];
> +
> + switch (type) {
> + case I40E_MODULE_TYPE_SFP:
> + status = i40e_aq_get_phy_register(hw,
> + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> + I40E_I2C_EEPROM_DEV_ADDR,
> + I40E_MODULE_SFF_8472_COMP,
> + &sff8472_comp, NULL);
> + if (status)
> + return -EIO;
> +
> + status = i40e_aq_get_phy_register(hw,
> + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> + I40E_I2C_EEPROM_DEV_ADDR,
> + I40E_MODULE_SFF_8472_SWAP,
> + &sff8472_swap, NULL);
> + if (status)
> + return -EIO;
> +
> + /* Check if the module requires address swap to access
> + * the other EEPROM memory page.
> + */
> + if (sff8472_swap & I40E_MODULE_SFF_ADDR_MODE) {
> + netdev_warn(vsi->netdev, "Module address swap to access page 0xA2 is not supported.\n");
> + modinfo->type = ETH_MODULE_SFF_8079;
> + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
> + } else if (sff8472_comp == 0x00) {
> + /* Module is not SFF-8472 compliant */
> + modinfo->type = ETH_MODULE_SFF_8079;
> + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
> + } else {
> + modinfo->type = ETH_MODULE_SFF_8472;
> + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
> + }
> + break;
> + case I40E_MODULE_TYPE_QSFP_PLUS:
> + /* Read from memory page 0. */
> + status = i40e_aq_get_phy_register(hw,
> + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> + 0,
> + I40E_MODULE_REVISION_ADDR,
> + &sff8636_rev, NULL);
> + if (status)
> + return -EIO;
> + /* Determine revision compliance byte */
> + if (sff8636_rev > 0x02) {
> + /* Module is SFF-8636 compliant */
> + modinfo->type = ETH_MODULE_SFF_8636;
> + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN;
> + } else {
> + modinfo->type = ETH_MODULE_SFF_8436;
> + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN;
> + }
> + break;
> + case I40E_MODULE_TYPE_QSFP28:
> + modinfo->type = ETH_MODULE_SFF_8636;
> + modinfo->eeprom_len = I40E_MODULE_QSFP_MAX_LEN;
> + break;
> + default:
> + netdev_err(vsi->netdev, "Module type unrecognized\n");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +/**
> + * i40e_get_module_eeprom - fills buffer with (Q)SFP+ module memory contents
> + * @netdev: network interface device structure
> + * @ee: EEPROM dump request structure
> + * @data: buffer to be filled with EEPROM contents
> + **/
> +static int i40e_get_module_eeprom(struct net_device *netdev,
> + struct ethtool_eeprom *ee,
> + u8 *data)
> +{
> + i40e_status status;
> + struct i40e_netdev_priv *np = netdev_priv(netdev);
> + struct i40e_vsi *vsi = np->vsi;
> + struct i40e_pf *pf = vsi->back;
> + struct i40e_hw *hw = &pf->hw;
> + bool is_sfp = false;
> + u32 value = 0;
> + int i;
> +
> + if (!ee || !ee->len || !data)
> + return -EINVAL;
> +
> + if (hw->phy.link_info.module_type[0] == I40E_MODULE_TYPE_SFP)
> + is_sfp = true;
> +
> + for (i = 0; i < ee->len; i++) {
> + u32 offset = i + ee->offset;
> + u32 addr = is_sfp ? I40E_I2C_EEPROM_DEV_ADDR : 0;
> +
> + /* Check if we need to access the other memory page */
> + if (is_sfp) {
> + if (offset >= ETH_MODULE_SFF_8079_LEN) {
> + offset -= ETH_MODULE_SFF_8079_LEN;
> + addr = I40E_I2C_EEPROM_DEV_ADDR2;
> + }
> + } else {
> + while (offset >= ETH_MODULE_SFF_8436_LEN) {
> + /* Compute memory page number and offset. */
> + offset -= ETH_MODULE_SFF_8436_LEN / 2;
> + addr++;
> + }
> + }
> +
> + status = i40e_aq_get_phy_register(hw,
> + I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> + addr, offset, &value, NULL);
> + if (status)
> + return -EIO;
> + data[i] = value;
> + }
> + return 0;
> +}
> +
> static const struct ethtool_ops i40e_ethtool_ops = {
> .get_drvinfo = i40e_get_drvinfo,
> .get_regs_len = i40e_get_regs_len,
> @@ -4234,6 +4386,8 @@ static const struct ethtool_ops i40e_ethtool_ops = {
> .set_rxfh = i40e_set_rxfh,
> .get_channels = i40e_get_channels,
> .set_channels = i40e_set_channels,
> + .get_module_info = i40e_get_module_info,
> + .get_module_eeprom = i40e_get_module_eeprom,
> .get_ts_info = i40e_get_ts_info,
> .get_priv_flags = i40e_get_priv_flags,
> .set_priv_flags = i40e_set_priv_flags,
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> index df613ea..6254ad5 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
> @@ -362,6 +362,15 @@ i40e_status i40e_aq_rx_ctl_write_register(struct i40e_hw *hw,
> u32 reg_addr, u32 reg_val,
> struct i40e_asq_cmd_details *cmd_details);
> void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val);
> +i40e_status i40e_aq_set_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 reg_val,
> + struct i40e_asq_cmd_details *cmd_details);
> +i40e_status i40e_aq_get_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 *reg_val,
> + struct i40e_asq_cmd_details *cmd_details);
> +
> i40e_status i40e_read_phy_register_clause22(struct i40e_hw *hw,
> u16 reg, u8 phy_addr, u16 *value);
> i40e_status i40e_write_phy_register_clause22(struct i40e_hw *hw,
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
> index 78fda11..8b0b9f8 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_type.h
> +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
> @@ -428,6 +428,18 @@ struct i40e_nvm_access {
> u8 data[1];
> };
>
> +/* (Q)SFP module access definitions */
> +#define I40E_I2C_EEPROM_DEV_ADDR 0xA0
> +#define I40E_I2C_EEPROM_DEV_ADDR2 0xA2
> +#define I40E_MODULE_TYPE_ADDR 0x00
> +#define I40E_MODULE_REVISION_ADDR 0x01
> +#define I40E_MODULE_SFF_8472_COMP 0x5E
> +#define I40E_MODULE_SFF_8472_SWAP 0x5C
> +#define I40E_MODULE_SFF_ADDR_MODE 0x04
> +#define I40E_MODULE_TYPE_QSFP_PLUS 0x0D
> +#define I40E_MODULE_TYPE_QSFP28 0x11
> +#define I40E_MODULE_QSFP_MAX_LEN 640
> +
> /* PCI bus types */
> enum i40e_bus_type {
> i40e_bus_type_unknown = 0,
> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
> index 709d114..b7715d4 100644
> --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
> @@ -244,6 +244,8 @@ enum i40e_admin_queue_opc {
> i40e_aqc_opc_set_phy_debug = 0x0622,
> i40e_aqc_opc_upload_ext_phy_fm = 0x0625,
> i40e_aqc_opc_run_phy_activity = 0x0626,
> + i40e_aqc_opc_set_phy_register = 0x0628,
> + i40e_aqc_opc_get_phy_register = 0x0629,
>
> /* NVM commands */
> i40e_aqc_opc_nvm_read = 0x0701,
> @@ -2046,6 +2048,22 @@ struct i40e_aqc_run_phy_activity {
>
> I40E_CHECK_CMD_LENGTH(i40e_aqc_run_phy_activity);
>
> +/* Set PHY Register command (0x0628) */
> +/* Get PHY Register command (0x0629) */
> +struct i40e_aqc_phy_register_access {
> + u8 phy_interface;
> +#define I40E_AQ_PHY_REG_ACCESS_INTERNAL 0
> +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL 1
> +#define I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE 2
> + u8 dev_address;
> + u8 reserved1[2];
> + u32 reg_address;
> + u32 reg_value;
> + u8 reserved2[4];
> +};
> +
> +I40E_CHECK_CMD_LENGTH(i40e_aqc_phy_register_access);
> +
> /* NVM Read command (indirect 0x0701)
> * NVM Erase commands (direct 0x0702)
> * NVM Update commands (indirect 0x0703)
> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c
> index 8d3a2bf..7df4610 100644
> --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c
> +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c
> @@ -1042,6 +1042,75 @@ void i40evf_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val)
> }
>
> /**
> + * i40evf_aq_set_phy_register
> + * @hw: pointer to the hw struct
> + * @phy_select: select which phy should be accessed
> + * @dev_addr: PHY device address
> + * @reg_addr: PHY register address
> + * @reg_val: new register value
> + * @cmd_details: pointer to command details structure or NULL
> + *
> + * Reset the external PHY.
> + **/
> +i40e_status i40evf_aq_set_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 reg_val,
> + struct i40e_asq_cmd_details *cmd_details)
> +{
> + struct i40e_aq_desc desc;
> + struct i40e_aqc_phy_register_access *cmd =
> + (struct i40e_aqc_phy_register_access *)&desc.params.raw;
> + i40e_status status;
> +
> + i40evf_fill_default_direct_cmd_desc(&desc,
> + i40e_aqc_opc_set_phy_register);
> +
> + cmd->phy_interface = phy_select;
> + cmd->dev_address = dev_addr;
> + cmd->reg_address = reg_addr;
> + cmd->reg_value = reg_val;
Same byteswapping comments...
> +
> + status = i40evf_asq_send_command(hw, &desc, NULL, 0, cmd_details);
> +
> + return status;
> +}
> +
> +/**
> + * i40evf_aq_get_phy_register
> + * @hw: pointer to the hw struct
> + * @phy_select: select which phy should be accessed
> + * @dev_addr: PHY device address
> + * @reg_addr: PHY register address
> + * @reg_val: read register value
> + * @cmd_details: pointer to command details structure or NULL
> + *
> + * Reset the external PHY.
> + **/
> +i40e_status i40evf_aq_get_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 *reg_val,
> + struct i40e_asq_cmd_details *cmd_details)
> +{
> + struct i40e_aq_desc desc;
> + struct i40e_aqc_phy_register_access *cmd =
> + (struct i40e_aqc_phy_register_access *)&desc.params.raw;
> + i40e_status status;
> +
> + i40evf_fill_default_direct_cmd_desc(&desc,
> + i40e_aqc_opc_get_phy_register);
> +
> + cmd->phy_interface = phy_select;
> + cmd->dev_address = dev_addr;
> + cmd->reg_address = reg_addr;
> +
> + status = i40evf_asq_send_command(hw, &desc, NULL, 0, cmd_details);
> + if (!status)
> + *reg_val = cmd->reg_value;
> +
> + return status;
> +}
> +
> +/**
> * i40e_aq_send_msg_to_pf
> * @hw: pointer to the hardware structure
> * @v_opcode: opcodes for VF-PF communication
> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
> index c9836bb..b624b59 100644
> --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
> +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h
> @@ -111,6 +111,15 @@ i40e_status i40evf_aq_rx_ctl_write_register(struct i40e_hw *hw,
> u32 reg_addr, u32 reg_val,
> struct i40e_asq_cmd_details *cmd_details);
> void i40evf_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val);
> +i40e_status i40e_aq_set_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 reg_val,
> + struct i40e_asq_cmd_details *cmd_details);
> +i40e_status i40e_aq_get_phy_register(struct i40e_hw *hw,
> + u8 phy_select, u8 dev_addr,
> + u32 reg_addr, u32 *reg_val,
> + struct i40e_asq_cmd_details *cmd_details);
> +
> i40e_status i40e_read_phy_register(struct i40e_hw *hw, u8 page,
> u16 reg, u8 phy_addr, u16 *value);
> i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page,
> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h
> index 412a32c..48eacf5 100644
> --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h
> +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h
> @@ -401,6 +401,18 @@ struct i40e_nvm_access {
> u8 data[1];
> };
>
> +/* (Q)SFP module access definitions */
> +#define I40E_I2C_EEPROM_DEV_ADDR 0xA0
> +#define I40E_I2C_EEPROM_DEV_ADDR2 0xA2
> +#define I40E_MODULE_TYPE_ADDR 0x00
> +#define I40E_MODULE_REVISION_ADDR 0x01
> +#define I40E_MODULE_SFF_8472_COMP 0x5E
> +#define I40E_MODULE_SFF_8472_SWAP 0x5C
> +#define I40E_MODULE_SFF_ADDR_MODE 0x04
> +#define I40E_MODULE_TYPE_QSFP_PLUS 0x0D
> +#define I40E_MODULE_TYPE_QSFP28 0x11
> +#define I40E_MODULE_QSFP_MAX_LEN 640
> +
> /* PCI bus types */
> enum i40e_bus_type {
> i40e_bus_type_unknown = 0,
>
More information about the Intel-wired-lan
mailing list