Skip to content

Commit

Permalink
amd-xgbe: Add ethtool support to retrieve SFP module info
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tom Lendacky authored and David S. Miller committed May 23, 2018
1 parent 67cea0c commit 53a1024
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 0 deletions.
18 changes: 18 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 changes: 137 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 "

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 changes: 13 additions & 0 deletions drivers/net/ethernet/amd/xgbe/xgbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
};
Expand Down

0 comments on commit 53a1024

Please sign in to comment.