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

Add support to get SFP module information using ethtool.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Tom Lendacky 2018-05-23 11:38:46 -05:00 committed by David S. Miller
parent 67cea0c922
commit 53a1024abf
4 changed files with 189 additions and 0 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
};