diff mbox

[next,S75-V2,12/12] i40e: Add support for 'ethtool -m'

Message ID 20170629083643.82018-12-alice.michael@intel.com
State Changes Requested
Headers show

Commit Message

Michael, Alice June 29, 2017, 8:36 a.m. UTC
From: Filip Sadowski <filip.sadowski@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@intel.com>
---
 drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 149 +++++++++++++++++++++++++
 1 file changed, 149 insertions(+)

Comments

Shannon Nelson July 5, 2017, 10:22 p.m. UTC | #1
On 6/29/2017 1:36 AM, Alice Michael wrote:
> From: Filip Sadowski <filip.sadowski@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@intel.com>
> ---
>   drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 149 +++++++++++++++++++++++++
>   1 file changed, 149 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> index b273c24..06f17ff 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
> @@ -4196,6 +4196,153 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
>   	return 0;
>   }
>   
> +#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_SFP		0x03
> +#define I40E_MODULE_TYPE_QSFP_PLUS	0x0D
> +#define I40E_MODULE_TYPE_QSFP28		0x11

Why aren't these defines in i40e_type.h?

sln

> +
> +/**
> + * 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->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
> +	      hw->aq.api_min_ver >= 7)) {
> +		netdev_err(vsi->netdev, "Module EEPROM memory read not supported. Please update the NVM image.\n");
> +		return -EINVAL;
> +	}
> +
> +	status = i40e_aq_get_phy_register(hw,
> +			I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> +			I40E_I2C_EEPROM_DEV_ADDR, I40E_MODULE_TYPE_ADDR,
> +			&type, NULL);
> +	if (status)
> +		return -EIO;
> +
> +	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:
> +		status = i40e_aq_get_phy_register(hw,
> +				I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
> +				I40E_I2C_EEPROM_DEV_ADDR,
> +				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 = ETH_MODULE_SFF_8636_LEN;
> +		} else {
> +			modinfo->type = ETH_MODULE_SFF_8436;
> +			modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
> +		}
> +		break;
> +	case I40E_MODULE_TYPE_QSFP28:
> +		modinfo->type = ETH_MODULE_SFF_8636;
> +		modinfo->eeprom_len = ETH_MODULE_SFF_8636_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;
> +	u32 value = 0;
> +	int i;
> +
> +	if (!ee || !ee->len || !data)
> +		return -EINVAL;
> +
> +	for (i = 0; i < ee->len; i++) {
> +		u32 offset = i + ee->offset;
> +		u32 addr = I40E_I2C_EEPROM_DEV_ADDR;
> +
> +		/* Check if we need to access the other memory page */
> +		if (offset >= ETH_MODULE_SFF_8079_LEN) {
> +			offset -= ETH_MODULE_SFF_8079_LEN;
> +			addr = I40E_I2C_EEPROM_DEV_ADDR2;
> +		}
> +
> +		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,
> @@ -4228,6 +4375,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 mbox

Patch

diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index b273c24..06f17ff 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -4196,6 +4196,153 @@  static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
 	return 0;
 }
 
+#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_SFP		0x03
+#define I40E_MODULE_TYPE_QSFP_PLUS	0x0D
+#define I40E_MODULE_TYPE_QSFP28		0x11
+
+/**
+ * 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->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
+	      hw->aq.api_min_ver >= 7)) {
+		netdev_err(vsi->netdev, "Module EEPROM memory read not supported. Please update the NVM image.\n");
+		return -EINVAL;
+	}
+
+	status = i40e_aq_get_phy_register(hw,
+			I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
+			I40E_I2C_EEPROM_DEV_ADDR, I40E_MODULE_TYPE_ADDR,
+			&type, NULL);
+	if (status)
+		return -EIO;
+
+	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:
+		status = i40e_aq_get_phy_register(hw,
+				I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE,
+				I40E_I2C_EEPROM_DEV_ADDR,
+				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 = ETH_MODULE_SFF_8636_LEN;
+		} else {
+			modinfo->type = ETH_MODULE_SFF_8436;
+			modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+		}
+		break;
+	case I40E_MODULE_TYPE_QSFP28:
+		modinfo->type = ETH_MODULE_SFF_8636;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8636_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;
+	u32 value = 0;
+	int i;
+
+	if (!ee || !ee->len || !data)
+		return -EINVAL;
+
+	for (i = 0; i < ee->len; i++) {
+		u32 offset = i + ee->offset;
+		u32 addr = I40E_I2C_EEPROM_DEV_ADDR;
+
+		/* Check if we need to access the other memory page */
+		if (offset >= ETH_MODULE_SFF_8079_LEN) {
+			offset -= ETH_MODULE_SFF_8079_LEN;
+			addr = I40E_I2C_EEPROM_DEV_ADDR2;
+		}
+
+		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,
@@ -4228,6 +4375,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,