Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71858acb authored by Aurélien Guillaume's avatar Aurélien Guillaume Committed by Jeff Kirsher
Browse files

ixgbe: implement SFF diagnostic monitoring via ethtool



This patch adds support for reading data from SFP+ modules over i2c.

Signed-off-by: default avatarAurélien Guillaume <footplus@gmail.com>
Signed-off-by: default avatarEmil Tantilov <emil.s.tantilov@intel.com>
Tested-by: default avatarPhil Schmitt <phillip.j.schmitt@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 51d0420b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -620,6 +620,7 @@ enum ixgbe_state_t {
	__IXGBE_DOWN,
	__IXGBE_SERVICE_SCHED,
	__IXGBE_IN_SFP_INIT,
	__IXGBE_READ_I2C,
};

struct ixgbe_cb {
+114 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <linux/uaccess.h>

#include "ixgbe.h"
#include "ixgbe_phy.h"


#define IXGBE_ALL_RAR_ENTRIES 16
@@ -2839,6 +2840,117 @@ static int ixgbe_set_channels(struct net_device *dev,
	return ixgbe_setup_tc(dev, netdev_get_num_tc(dev));
}

static int ixgbe_get_module_info(struct net_device *dev,
				       struct ethtool_modinfo *modinfo)
{
	struct ixgbe_adapter *adapter = netdev_priv(dev);
	struct ixgbe_hw *hw = &adapter->hw;
	u32 status;
	u8 sff8472_rev, addr_mode;
	int ret_val = 0;
	bool page_swap = false;

	/* avoid concurent i2c reads */
	while (test_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
		msleep(100);

	/* used by the service task */
	set_bit(__IXGBE_READ_I2C, &adapter->state);

	/* Check whether we support SFF-8472 or not */
	status = hw->phy.ops.read_i2c_eeprom(hw,
					     IXGBE_SFF_SFF_8472_COMP,
					     &sff8472_rev);
	if (status != 0) {
		ret_val = -EIO;
		goto err_out;
	}

	/* addressing mode is not supported */
	status = hw->phy.ops.read_i2c_eeprom(hw,
					     IXGBE_SFF_SFF_8472_SWAP,
					     &addr_mode);
	if (status != 0) {
		ret_val = -EIO;
		goto err_out;
	}

	if (addr_mode & IXGBE_SFF_ADDRESSING_MODE) {
		e_err(drv, "Address change required to access page 0xA2, but not supported. Please report the module type to the driver maintainers.\n");
		page_swap = true;
	}

	if (sff8472_rev == IXGBE_SFF_SFF_8472_UNSUP || page_swap) {
		/* We have a SFP, but it does not support SFF-8472 */
		modinfo->type = ETH_MODULE_SFF_8079;
		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
	} else {
		/* We have a SFP which supports a revision of SFF-8472. */
		modinfo->type = ETH_MODULE_SFF_8472;
		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
	}

err_out:
	clear_bit(__IXGBE_READ_I2C, &adapter->state);
	return ret_val;
}

static int ixgbe_get_module_eeprom(struct net_device *dev,
					 struct ethtool_eeprom *ee,
					 u8 *data)
{
	struct ixgbe_adapter *adapter = netdev_priv(dev);
	struct ixgbe_hw *hw = &adapter->hw;
	u32 status = IXGBE_ERR_PHY_ADDR_INVALID;
	u8 databyte = 0xFF;
	int i = 0;
	int ret_val = 0;

	/* ixgbe_get_module_info is called before this function in all
	 * cases, so we do not need any checks we already do above,
	 * and can trust ee->len to be a known value.
	 */

	while (test_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
		msleep(100);
	set_bit(__IXGBE_READ_I2C, &adapter->state);

	/* Read the first block, SFF-8079 */
	for (i = 0; i < ETH_MODULE_SFF_8079_LEN; i++) {
		status = hw->phy.ops.read_i2c_eeprom(hw, i, &databyte);
		if (status != 0) {
			/* Error occured while reading module */
			ret_val = -EIO;
			goto err_out;
		}
		data[i] = databyte;
	}

	/* If the second block is requested, check if SFF-8472 is supported. */
	if (ee->len == ETH_MODULE_SFF_8472_LEN) {
		if (data[IXGBE_SFF_SFF_8472_COMP] == IXGBE_SFF_SFF_8472_UNSUP)
			return -EOPNOTSUPP;

		/* Read the second block, SFF-8472 */
		for (i = ETH_MODULE_SFF_8079_LEN;
		     i < ETH_MODULE_SFF_8472_LEN; i++) {
			status = hw->phy.ops.read_i2c_sff8472(hw,
				i - ETH_MODULE_SFF_8079_LEN, &databyte);
			if (status != 0) {
				/* Error occured while reading module */
				ret_val = -EIO;
				goto err_out;
			}
			data[i] = databyte;
		}
	}

err_out:
	clear_bit(__IXGBE_READ_I2C, &adapter->state);

	return ret_val;
}

static const struct ethtool_ops ixgbe_ethtool_ops = {
	.get_settings           = ixgbe_get_settings,
	.set_settings           = ixgbe_set_settings,
@@ -2870,6 +2982,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
	.get_channels		= ixgbe_get_channels,
	.set_channels		= ixgbe_set_channels,
	.get_ts_info		= ixgbe_get_ts_info,
	.get_module_info	= ixgbe_get_module_info,
	.get_module_eeprom	= ixgbe_get_module_eeprom,
};

void ixgbe_set_ethtool_ops(struct net_device *netdev)
+4 −0
Original line number Diff line number Diff line
@@ -5709,6 +5709,10 @@ static void ixgbe_sfp_detection_subtask(struct ixgbe_adapter *adapter)
	    !(adapter->flags2 & IXGBE_FLAG2_SFP_NEEDS_RESET))
		return;

	/* concurent i2c reads are not supported */
	if (test_bit(__IXGBE_READ_I2C, &adapter->state))
		return;

	/* someone else is in init, wait until next service event */
	if (test_and_set_bit(__IXGBE_IN_SFP_INIT, &adapter->state))
		return;