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

Commit 53a1024a authored by Tom Lendacky's avatar Tom Lendacky Committed by David S. Miller
Browse files

amd-xgbe: Add ethtool support to retrieve SFP module info



Add support to get SFP module information using ethtool.

Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 67cea0c9
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -626,6 +626,22 @@ static int xgbe_get_ts_info(struct net_device *netdev,
	return 0;
}

static int xgbe_get_module_info(struct net_device *netdev,
				struct ethtool_modinfo *modinfo)
{
	struct xgbe_prv_data *pdata = netdev_priv(netdev);

	return pdata->phy_if.module_info(pdata, modinfo);
}

static int xgbe_get_module_eeprom(struct net_device *netdev,
				  struct ethtool_eeprom *eeprom, u8 *data)
{
	struct xgbe_prv_data *pdata = netdev_priv(netdev);

	return pdata->phy_if.module_eeprom(pdata, eeprom, data);
}

static const struct ethtool_ops xgbe_ethtool_ops = {
	.get_drvinfo = xgbe_get_drvinfo,
	.get_msglevel = xgbe_get_msglevel,
@@ -646,6 +662,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = {
	.get_ts_info = xgbe_get_ts_info,
	.get_link_ksettings = xgbe_get_link_ksettings,
	.set_link_ksettings = xgbe_set_link_ksettings,
	.get_module_info = xgbe_get_module_info,
	.get_module_eeprom = xgbe_get_module_eeprom,
};

const struct ethtool_ops *xgbe_get_ethtool_ops(void)
+21 −0
Original line number Diff line number Diff line
@@ -126,6 +126,24 @@
#include "xgbe.h"
#include "xgbe-common.h"

static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata,
				  struct ethtool_eeprom *eeprom, u8 *data)
{
	if (!pdata->phy_if.phy_impl.module_eeprom)
		return -ENXIO;

	return pdata->phy_if.phy_impl.module_eeprom(pdata, eeprom, data);
}

static int xgbe_phy_module_info(struct xgbe_prv_data *pdata,
				struct ethtool_modinfo *modinfo)
{
	if (!pdata->phy_if.phy_impl.module_info)
		return -ENXIO;

	return pdata->phy_if.phy_impl.module_info(pdata, modinfo);
}

static void xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata)
{
	int reg;
@@ -1639,4 +1657,7 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if)
	phy_if->phy_valid_speed = xgbe_phy_valid_speed;

	phy_if->an_isr          = xgbe_an_combined_isr;

	phy_if->module_info     = xgbe_phy_module_info;
	phy_if->module_eeprom   = xgbe_phy_module_eeprom;
}
+137 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@
#include <linux/kmod.h>
#include <linux/mdio.h>
#include <linux/phy.h>
#include <linux/ethtool.h>

#include "xgbe.h"
#include "xgbe-common.h"
@@ -270,6 +271,15 @@ struct xgbe_sfp_eeprom {
	u8 vendor[32];
};

#define XGBE_SFP_DIAGS_SUPPORTED(_x)			\
	((_x)->extd[XGBE_SFP_EXTD_SFF_8472] &&		\
	 !((_x)->extd[XGBE_SFP_EXTD_DIAG] & XGBE_SFP_EXTD_DIAG_ADDR_CHANGE))

#define XGBE_SFP_EEPROM_BASE_LEN	256
#define XGBE_SFP_EEPROM_DIAG_LEN	256
#define XGBE_SFP_EEPROM_MAX		(XGBE_SFP_EEPROM_BASE_LEN +	\
					 XGBE_SFP_EEPROM_DIAG_LEN)

#define XGBE_BEL_FUSE_VENDOR	"BEL-FUSE        "
#define XGBE_BEL_FUSE_PARTNO	"1GBT-SFP06      "

@@ -1301,6 +1311,130 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata)
	xgbe_phy_put_comm_ownership(pdata);
}

static int xgbe_phy_module_eeprom(struct xgbe_prv_data *pdata,
				  struct ethtool_eeprom *eeprom, u8 *data)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	u8 eeprom_addr, eeprom_data[XGBE_SFP_EEPROM_MAX];
	struct xgbe_sfp_eeprom *sfp_eeprom;
	unsigned int i, j, rem;
	int ret;

	rem = eeprom->len;

	if (!eeprom->len) {
		ret = -EINVAL;
		goto done;
	}

	if ((eeprom->offset + eeprom->len) > XGBE_SFP_EEPROM_MAX) {
		ret = -EINVAL;
		goto done;
	}

	if (phy_data->port_mode != XGBE_PORT_MODE_SFP) {
		ret = -ENXIO;
		goto done;
	}

	if (!netif_running(pdata->netdev)) {
		ret = -EIO;
		goto done;
	}

	if (phy_data->sfp_mod_absent) {
		ret = -EIO;
		goto done;
	}

	ret = xgbe_phy_get_comm_ownership(pdata);
	if (ret) {
		ret = -EIO;
		goto done;
	}

	ret = xgbe_phy_sfp_get_mux(pdata);
	if (ret) {
		netdev_err(pdata->netdev, "I2C error setting SFP MUX\n");
		ret = -EIO;
		goto put_own;
	}

	/* Read the SFP serial ID eeprom */
	eeprom_addr = 0;
	ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_SERIAL_ID_ADDRESS,
				&eeprom_addr, sizeof(eeprom_addr),
				eeprom_data, XGBE_SFP_EEPROM_BASE_LEN);
	if (ret) {
		netdev_err(pdata->netdev,
			   "I2C error reading SFP EEPROM\n");
		ret = -EIO;
		goto put_mux;
	}

	sfp_eeprom = (struct xgbe_sfp_eeprom *)eeprom_data;

	if (XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom)) {
		/* Read the SFP diagnostic eeprom */
		eeprom_addr = 0;
		ret = xgbe_phy_i2c_read(pdata, XGBE_SFP_DIAG_INFO_ADDRESS,
					&eeprom_addr, sizeof(eeprom_addr),
					eeprom_data + XGBE_SFP_EEPROM_BASE_LEN,
					XGBE_SFP_EEPROM_DIAG_LEN);
		if (ret) {
			netdev_err(pdata->netdev,
				   "I2C error reading SFP DIAGS\n");
			ret = -EIO;
			goto put_mux;
		}
	}

	for (i = 0, j = eeprom->offset; i < eeprom->len; i++, j++) {
		if ((j >= XGBE_SFP_EEPROM_BASE_LEN) &&
		    !XGBE_SFP_DIAGS_SUPPORTED(sfp_eeprom))
			break;

		data[i] = eeprom_data[j];
		rem--;
	}

put_mux:
	xgbe_phy_sfp_put_mux(pdata);

put_own:
	xgbe_phy_put_comm_ownership(pdata);

done:
	eeprom->len -= rem;

	return ret;
}

static int xgbe_phy_module_info(struct xgbe_prv_data *pdata,
				struct ethtool_modinfo *modinfo)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;

	if (phy_data->port_mode != XGBE_PORT_MODE_SFP)
		return -ENXIO;

	if (!netif_running(pdata->netdev))
		return -EIO;

	if (phy_data->sfp_mod_absent)
		return -EIO;

	if (XGBE_SFP_DIAGS_SUPPORTED(&phy_data->sfp_eeprom)) {
		modinfo->type = ETH_MODULE_SFF_8472;
		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
	} else {
		modinfo->type = ETH_MODULE_SFF_8079;
		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
	}

	return 0;
}

static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
{
	struct ethtool_link_ksettings *lks = &pdata->phy.lks;
@@ -3196,4 +3330,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)

	phy_impl->kr_training_pre	= xgbe_phy_kr_training_pre;
	phy_impl->kr_training_post	= xgbe_phy_kr_training_post;

	phy_impl->module_info		= xgbe_phy_module_info;
	phy_impl->module_eeprom		= xgbe_phy_module_eeprom;
}
+13 −0
Original line number Diff line number Diff line
@@ -835,6 +835,7 @@ struct xgbe_hw_if {
 *   Optional routines:
 *     an_pre, an_post
 *     kr_training_pre, kr_training_post
 *     module_info, module_eeprom
 */
struct xgbe_phy_impl_if {
	/* Perform Setup/teardown actions */
@@ -883,6 +884,12 @@ struct xgbe_phy_impl_if {
	/* Pre/Post KR training enablement support */
	void (*kr_training_pre)(struct xgbe_prv_data *);
	void (*kr_training_post)(struct xgbe_prv_data *);

	/* SFP module related info */
	int (*module_info)(struct xgbe_prv_data *pdata,
			   struct ethtool_modinfo *modinfo);
	int (*module_eeprom)(struct xgbe_prv_data *pdata,
			     struct ethtool_eeprom *eeprom, u8 *data);
};

struct xgbe_phy_if {
@@ -905,6 +912,12 @@ struct xgbe_phy_if {
	/* For single interrupt support */
	irqreturn_t (*an_isr)(struct xgbe_prv_data *);

	/* For ethtool PHY support */
	int (*module_info)(struct xgbe_prv_data *pdata,
			   struct ethtool_modinfo *modinfo);
	int (*module_eeprom)(struct xgbe_prv_data *pdata,
			     struct ethtool_eeprom *eeprom, u8 *data);

	/* PHY implementation specific services */
	struct xgbe_phy_impl_if phy_impl;
};