mlxsw: spectrum: Add support for access cable info via ethtool
authorArkadi Sharshevsky <arkadis@mellanox.com>
Wed, 14 Jun 2017 07:27:40 +0000 (09:27 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 14 Jun 2017 19:16:30 +0000 (15:16 -0400)
Add support for access cable info via ethtool.

Signed-off-by: Arkadi Sharshevsky <arkadis@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/spectrum.c

index 0e51c3693243539de1b7bf973db78a036e0bbf99..60bf8f27cc00ecc6bbed3685a14dd3aa8a96fd78 100644 (file)
@@ -2519,6 +2519,135 @@ out:
        return err;
 }
 
+#define MLXSW_SP_QSFP_I2C_ADDR 0x50
+
+static int mlxsw_sp_query_module_eeprom(struct mlxsw_sp_port *mlxsw_sp_port,
+                                       u16 offset, u16 size, void *data,
+                                       unsigned int *p_read_size)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char eeprom_tmp[MLXSW_SP_REG_MCIA_EEPROM_SIZE];
+       char mcia_pl[MLXSW_REG_MCIA_LEN];
+       int status;
+       int err;
+
+       size = min_t(u16, size, MLXSW_SP_REG_MCIA_EEPROM_SIZE);
+       mlxsw_reg_mcia_pack(mcia_pl, mlxsw_sp_port->mapping.module,
+                           0, 0, offset, size, MLXSW_SP_QSFP_I2C_ADDR);
+
+       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mcia), mcia_pl);
+       if (err)
+               return err;
+
+       status = mlxsw_reg_mcia_status_get(mcia_pl);
+       if (status)
+               return -EIO;
+
+       mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp);
+       memcpy(data, eeprom_tmp, size);
+       *p_read_size = size;
+
+       return 0;
+}
+
+enum mlxsw_sp_eeprom_module_info_rev_id {
+       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_UNSPC      = 0x00,
+       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8436       = 0x01,
+       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636       = 0x03,
+};
+
+enum mlxsw_sp_eeprom_module_info_id {
+       MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP              = 0x03,
+       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP             = 0x0C,
+       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS        = 0x0D,
+       MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28           = 0x11,
+};
+
+enum mlxsw_sp_eeprom_module_info {
+       MLXSW_SP_EEPROM_MODULE_INFO_ID,
+       MLXSW_SP_EEPROM_MODULE_INFO_REV_ID,
+       MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
+};
+
+static int mlxsw_sp_get_module_info(struct net_device *netdev,
+                                   struct ethtool_modinfo *modinfo)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
+       u8 module_info[MLXSW_SP_EEPROM_MODULE_INFO_SIZE];
+       u8 module_rev_id, module_id;
+       unsigned int read_size;
+       int err;
+
+       err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, 0,
+                                          MLXSW_SP_EEPROM_MODULE_INFO_SIZE,
+                                          module_info, &read_size);
+       if (err)
+               return err;
+
+       if (read_size < MLXSW_SP_EEPROM_MODULE_INFO_SIZE)
+               return -EIO;
+
+       module_rev_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_REV_ID];
+       module_id = module_info[MLXSW_SP_EEPROM_MODULE_INFO_ID];
+
+       switch (module_id) {
+       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP:
+               modinfo->type       = ETH_MODULE_SFF_8436;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+               break;
+       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP_PLUS:
+       case MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28:
+               if (module_id  == MLXSW_SP_EEPROM_MODULE_INFO_ID_QSFP28 ||
+                   module_rev_id >= MLXSW_SP_EEPROM_MODULE_INFO_REV_ID_8636) {
+                       modinfo->type       = ETH_MODULE_SFF_8636;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
+               } else {
+                       modinfo->type       = ETH_MODULE_SFF_8436;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
+               }
+               break;
+       case MLXSW_SP_EEPROM_MODULE_INFO_ID_SFP:
+               modinfo->type       = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mlxsw_sp_get_module_eeprom(struct net_device *netdev,
+                                     struct ethtool_eeprom *ee,
+                                     u8 *data)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(netdev);
+       int offset = ee->offset;
+       unsigned int read_size;
+       int i = 0;
+       int err;
+
+       if (!ee->len)
+               return -EINVAL;
+
+       memset(data, 0, ee->len);
+
+       while (i < ee->len) {
+               err = mlxsw_sp_query_module_eeprom(mlxsw_sp_port, offset,
+                                                  ee->len - i, data + i,
+                                                  &read_size);
+               if (err) {
+                       netdev_err(mlxsw_sp_port->dev, "Eeprom query failed\n");
+                       return err;
+               }
+
+               i += read_size;
+               offset += read_size;
+       }
+
+       return 0;
+}
+
 static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
        .get_drvinfo            = mlxsw_sp_port_get_drvinfo,
        .get_link               = ethtool_op_get_link,
@@ -2531,6 +2660,8 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
        .get_link_ksettings     = mlxsw_sp_port_get_link_ksettings,
        .set_link_ksettings     = mlxsw_sp_port_set_link_ksettings,
        .flash_device           = mlxsw_sp_flash_device,
+       .get_module_info        = mlxsw_sp_get_module_info,
+       .get_module_eeprom      = mlxsw_sp_get_module_eeprom,
 };
 
 static int