[PATCH net-next 3/3] Add capability to retrieve plug-in module EEPROM

Subsystems: networking drivers, sfc network driver, the rest

STALE5218d

2 messages, 2 authors, 2012-04-24 · open the first message on its own page

[PATCH net-next 3/3] Add capability to retrieve plug-in module EEPROM

From: Stuart Hodgson <hidden>
Date: 2012-04-19 15:39:57

Currently allows for SFP+ eeprom to be returned using the ethtool API.
This can be extended in future to handle different eeprom formats
and sizes

Signed-off-by: Stuart Hodgson <redacted>
---
 drivers/net/ethernet/sfc/ethtool.c    |   35 ++++++++++
 drivers/net/ethernet/sfc/mcdi_phy.c   |  111 +++++++++++++++++++++++++++++++++
 drivers/net/ethernet/sfc/net_driver.h |    5 ++
 3 files changed, 151 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index f22f45f..60e718f 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -1108,6 +1108,39 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
 	return 0;
 }

+static int efx_ethtool_get_module_eeprom(struct net_device *net_dev,
+					 struct ethtool_eeprom *ee,
+					 u8 *data)
+{
+	struct efx_nic *efx = netdev_priv(net_dev);
+	int ret;
+
+	if (!efx->phy_op || !efx->phy_op->get_module_eeprom)
+		return -EOPNOTSUPP;
+
+	mutex_lock(&efx->mac_lock);
+	ret = efx->phy_op->get_module_eeprom(efx, ee, data);
+	mutex_unlock(&efx->mac_lock);
+
+	return ret;
+}
+
+static int efx_ethtool_get_module_info(struct net_device *net_dev,
+				       struct ethtool_modinfo *modinfo)
+{
+	struct efx_nic *efx = netdev_priv(net_dev);
+	int ret;
+
+	if (!efx->phy_op || !efx->phy_op->get_module_info)
+		return 0;
+
+	mutex_lock(&efx->mac_lock);
+	ret = efx->phy_op->get_module_info(efx, modinfo);
+	mutex_unlock(&efx->mac_lock);
+
+	return ret;
+}
+
 const struct ethtool_ops efx_ethtool_ops = {
 	.get_settings		= efx_ethtool_get_settings,
 	.set_settings		= efx_ethtool_set_settings,
@@ -1137,4 +1170,6 @@ const struct ethtool_ops efx_ethtool_ops = {
 	.get_rxfh_indir_size	= efx_ethtool_get_rxfh_indir_size,
 	.get_rxfh_indir		= efx_ethtool_get_rxfh_indir,
 	.set_rxfh_indir		= efx_ethtool_set_rxfh_indir,
+	.get_module_info	= efx_ethtool_get_module_info,
+	.get_module_eeprom	= efx_ethtool_get_module_eeprom,
 };
diff --git a/drivers/net/ethernet/sfc/mcdi_phy.c b/drivers/net/ethernet/sfc/mcdi_phy.c
index 7bcad89..4bac56c 100644
--- a/drivers/net/ethernet/sfc/mcdi_phy.c
+++ b/drivers/net/ethernet/sfc/mcdi_phy.c
@@ -304,6 +304,26 @@ static u32 mcdi_to_ethtool_media(u32 media)
 	}
 }

+static u32 mcdi_to_module_eeprom_len(u32 media)
+{
+	switch (media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		return ETH_MODULE_SFF_8079_LEN;
+	default:
+		return 0;
+	}
+}
+
+static u32 mcdi_to_module_eeprom_type(u32 media)
+{
+	switch (media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		return ETH_MODULE_SFF_8079;
+	default:
+		return 0;
+	}
+}
+
 static int efx_mcdi_phy_probe(struct efx_nic *efx)
 {
 	struct efx_mcdi_phy_data *phy_data;
@@ -739,6 +759,95 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
 	return NULL;
 }

+#define SFP_PAGE_SIZE	128
+#define SFP_NUM_PAGES	2
+static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
+					  struct ethtool_eeprom *ee, u8 *data)
+{
+	u8 outbuf[MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX];
+	u8 inbuf[MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN];
+	size_t outlen;
+	int rc;
+	unsigned int payload_len;
+	unsigned int copied = 0;
+	unsigned int space_remaining = ee->len;
+	unsigned int page;
+	unsigned int page_off;
+	unsigned int to_copy;
+	u8 *user_data = data;
+
+	if (ee->offset > (SFP_PAGE_SIZE * SFP_NUM_PAGES))
+		return -EINVAL;
+
+	page_off = (ee->offset % SFP_PAGE_SIZE);
+	page = (ee->offset > SFP_PAGE_SIZE) ? 1 : 0;
+
+	while (space_remaining && (page < SFP_NUM_PAGES)) {
+
+		MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
+
+		rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO,
+				  inbuf, sizeof(inbuf),
+				  outbuf, sizeof(outbuf),
+				  &outlen);
+
+		if (rc)
+			return rc;
+
+		/* Copy as much as we can into data */
+		if (outlen < SFP_PAGE_SIZE ||
+		    outlen < MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMIN ||
+		    outlen > MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX)
+			return -EIO;
+
+		payload_len = MCDI_DWORD(outbuf,
+					 GET_PHY_MEDIA_INFO_OUT_DATALEN);
+
+		payload_len -= page_off;
+		to_copy = (space_remaining < payload_len) ?
+				space_remaining : payload_len;
+
+		memcpy(user_data,
+		       (outbuf + page_off +
+		       MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST),
+		       to_copy);
+
+		space_remaining -= to_copy;
+		user_data += to_copy;
+		copied += to_copy;
+		page_off = 0;
+		page++;
+	}
+
+	ee->len = copied;
+
+	return 0;
+}
+
+static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
+					struct ethtool_modinfo *modinfo)
+{
+	/* This will return a length of the eeprom
+	 * type of the module that was detected during the probe,
+	 * if not modules inserted then phy_data will be NULL */
+	struct efx_mcdi_phy_data *phy_cfg;
+
+	phy_cfg = efx->phy_data;
+	modinfo->eeprom_len = 0;
+	modinfo->type = 0;
+
+	switch (phy_cfg->media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		modinfo->type = ETH_MODULE_SFF_8079;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 const struct efx_phy_operations efx_mcdi_phy_ops = {
 	.probe		= efx_mcdi_phy_probe,
 	.init		= efx_port_dummy_op_int,
@@ -751,4 +860,6 @@ const struct efx_phy_operations efx_mcdi_phy_ops = {
 	.test_alive	= efx_mcdi_phy_test_alive,
 	.run_tests	= efx_mcdi_phy_run_tests,
 	.test_name	= efx_mcdi_phy_test_name,
+	.get_module_eeprom	= efx_mcdi_phy_get_module_eeprom,
+	.get_module_info	= efx_mcdi_phy_get_module_info,
 };
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index f0385e1..0af8c47 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -522,6 +522,11 @@ struct efx_phy_operations {
 	int (*test_alive) (struct efx_nic *efx);
 	const char *(*test_name) (struct efx_nic *efx, unsigned int index);
 	int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags);
+	int (*get_module_eeprom) (struct efx_nic *efx,
+				  struct ethtool_eeprom *ee,
+				  u8 *data);
+	int (*get_module_info) (struct efx_nic *efx,
+				struct ethtool_modinfo *modinfo);
 };

 /**
-- 
1.7.1

Re: [PATCH net-next 3/3] Add capability to retrieve plug-in module EEPROM

From: Ben Hutchings <hidden>
Date: 2012-04-24 17:43:24

On Thu, 2012-04-19 at 16:39 +0100, Stuart Hodgson wrote:
Currently allows for SFP+ eeprom to be returned using the ethtool API.
This can be extended in future to handle different eeprom formats
and sizes
[...]
quoted hunk
--- a/drivers/net/ethernet/sfc/mcdi_phy.c
+++ b/drivers/net/ethernet/sfc/mcdi_phy.c
@@ -304,6 +304,26 @@ static u32 mcdi_to_ethtool_media(u32 media)
 	}
 }

+static u32 mcdi_to_module_eeprom_len(u32 media)
+{
+	switch (media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		return ETH_MODULE_SFF_8079_LEN;
+	default:
+		return 0;
+	}
+}
+
+static u32 mcdi_to_module_eeprom_type(u32 media)
+{
+	switch (media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		return ETH_MODULE_SFF_8079;
+	default:
+		return 0;
+	}
+}
These functions ended up unused, so don't add them.  You should have got
a compiler warning about this...
quoted hunk
 static int efx_mcdi_phy_probe(struct efx_nic *efx)
 {
 	struct efx_mcdi_phy_data *phy_data;
@@ -739,6 +759,95 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
 	return NULL;
 }

+#define SFP_PAGE_SIZE	128
+#define SFP_NUM_PAGES	2
+static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
+					  struct ethtool_eeprom *ee, u8 *data)
+{
+	u8 outbuf[MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX];
+	u8 inbuf[MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN];
+	size_t outlen;
+	int rc;
+	unsigned int payload_len;
+	unsigned int copied = 0;
+	unsigned int space_remaining = ee->len;
+	unsigned int page;
+	unsigned int page_off;
+	unsigned int to_copy;
+	u8 *user_data = data;
+
+	if (ee->offset > (SFP_PAGE_SIZE * SFP_NUM_PAGES))
+		return -EINVAL;
Please also add this sanity-check:

	BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN);
+	page_off = (ee->offset % SFP_PAGE_SIZE);
+	page = (ee->offset > SFP_PAGE_SIZE) ? 1 : 0;
Off-by-one error; the comparison should be >=.  Why not use the
straightforward calculation:

	page = ee->offset / SFP_PAGE_SIZE;

[...]
+static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
+					struct ethtool_modinfo *modinfo)
+{
+	/* This will return a length of the eeprom
+	 * type of the module that was detected during the probe,
+	 * if not modules inserted then phy_data will be NULL */
+	struct efx_mcdi_phy_data *phy_cfg;
+
+	phy_cfg = efx->phy_data;
+	modinfo->eeprom_len = 0;
+	modinfo->type = 0;
+
+	switch (phy_cfg->media) {
+	case MC_CMD_MEDIA_SFP_PLUS:
+		modinfo->type = ETH_MODULE_SFF_8079;
+		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
+		break;
+	default:
The default case should return -EOPNOTSUPP, just as if we didn't
implement this operation at all.  And there's no need to set
modinfo->{type,eeprom_len} to 0 in that case.

Ben.
+		break;
+	}
+
+	return 0;
+}
[...]

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help