diff options
author | Tom Lendacky <thomas.lendacky@amd.com> | 2018-05-23 19:38:46 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-05-23 23:33:00 +0300 |
commit | 53a1024abf3e4cb3310a54b08c48323649158c13 (patch) | |
tree | 020d15a8069ff5bd976e5beb7edb331bc70d408c /drivers | |
parent | 67cea0c92264a657230ba14e1956c34132ca6161 (diff) | |
download | linux-53a1024abf3e4cb3310a54b08c48323649158c13.tar.xz |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 137 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe.h | 13 |
4 files changed, 189 insertions, 0 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index ff397bb25042..57394b77b2d3 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -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) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 1b45cd73a258..9c39c72d25ab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -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; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index cb15cafd5209..141bb13dda26 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -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 @@ put: 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; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 54e43ad32d26..f0f455b79ff9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -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; }; |