net/mlx5e: Add ethtool support for dump module EEPROM
Add query MCIA, PMLP registers infrastructure and commands. Add ethtool support for get_module_info() and get_module_eeprom() callbacks. Signed-off-by: Gal Pressman <galp@mellanox.com> Signed-off-by: Saeed Mahameed <saeedm@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
da54d24ec3
Коммит
bb64143eee
|
@ -1159,6 +1159,84 @@ static int mlx5e_set_phys_id(struct net_device *dev,
|
|||
return mlx5_set_port_beacon(mdev, beacon_duration);
|
||||
}
|
||||
|
||||
static int mlx5e_get_module_info(struct net_device *netdev,
|
||||
struct ethtool_modinfo *modinfo)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||
struct mlx5_core_dev *dev = priv->mdev;
|
||||
int size_read = 0;
|
||||
u8 data[4];
|
||||
|
||||
size_read = mlx5_query_module_eeprom(dev, 0, 2, data);
|
||||
if (size_read < 2)
|
||||
return -EIO;
|
||||
|
||||
/* data[0] = identifier byte */
|
||||
switch (data[0]) {
|
||||
case MLX5_MODULE_ID_QSFP:
|
||||
modinfo->type = ETH_MODULE_SFF_8436;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
||||
break;
|
||||
case MLX5_MODULE_ID_QSFP_PLUS:
|
||||
case MLX5_MODULE_ID_QSFP28:
|
||||
/* data[1] = revision id */
|
||||
if (data[0] == MLX5_MODULE_ID_QSFP28 || data[1] >= 0x3) {
|
||||
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 MLX5_MODULE_ID_SFP:
|
||||
modinfo->type = ETH_MODULE_SFF_8472;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
||||
break;
|
||||
default:
|
||||
netdev_err(priv->netdev, "%s: cable type not recognized:0x%x\n",
|
||||
__func__, data[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx5e_get_module_eeprom(struct net_device *netdev,
|
||||
struct ethtool_eeprom *ee,
|
||||
u8 *data)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
int offset = ee->offset;
|
||||
int size_read;
|
||||
int i = 0;
|
||||
|
||||
if (!ee->len)
|
||||
return -EINVAL;
|
||||
|
||||
memset(data, 0, ee->len);
|
||||
|
||||
while (i < ee->len) {
|
||||
size_read = mlx5_query_module_eeprom(mdev, offset, ee->len - i,
|
||||
data + i);
|
||||
|
||||
if (!size_read)
|
||||
/* Done reading */
|
||||
return 0;
|
||||
|
||||
if (size_read < 0) {
|
||||
netdev_err(priv->netdev, "%s: mlx5_query_eeprom failed:0x%x\n",
|
||||
__func__, size_read);
|
||||
return 0;
|
||||
}
|
||||
|
||||
i += size_read;
|
||||
offset += size_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ethtool_ops mlx5e_ethtool_ops = {
|
||||
.get_drvinfo = mlx5e_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
|
@ -1186,4 +1264,6 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
|
|||
.set_phys_id = mlx5e_set_phys_id,
|
||||
.get_wol = mlx5e_get_wol,
|
||||
.set_wol = mlx5e_set_wol,
|
||||
.get_module_info = mlx5e_get_module_info,
|
||||
.get_module_eeprom = mlx5e_get_module_eeprom,
|
||||
};
|
||||
|
|
|
@ -310,6 +310,82 @@ void mlx5_query_port_oper_mtu(struct mlx5_core_dev *dev, int *oper_mtu,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mlx5_query_port_oper_mtu);
|
||||
|
||||
static int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num)
|
||||
{
|
||||
u32 out[MLX5_ST_SZ_DW(pmlp_reg)];
|
||||
u32 in[MLX5_ST_SZ_DW(pmlp_reg)];
|
||||
int module_mapping;
|
||||
int err;
|
||||
|
||||
memset(in, 0, sizeof(in));
|
||||
|
||||
MLX5_SET(pmlp_reg, in, local_port, 1);
|
||||
|
||||
err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
|
||||
MLX5_REG_PMLP, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
module_mapping = MLX5_GET(pmlp_reg, out, lane0_module_mapping);
|
||||
*module_num = module_mapping & MLX5_EEPROM_IDENTIFIER_BYTE_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
|
||||
u16 offset, u16 size, u8 *data)
|
||||
{
|
||||
u32 out[MLX5_ST_SZ_DW(mcia_reg)];
|
||||
u32 in[MLX5_ST_SZ_DW(mcia_reg)];
|
||||
int module_num;
|
||||
u16 i2c_addr;
|
||||
int status;
|
||||
int err;
|
||||
void *ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
|
||||
|
||||
err = mlx5_query_module_num(dev, &module_num);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(in, 0, sizeof(in));
|
||||
size = min_t(int, size, MLX5_EEPROM_MAX_BYTES);
|
||||
|
||||
if (offset < MLX5_EEPROM_PAGE_LENGTH &&
|
||||
offset + size > MLX5_EEPROM_PAGE_LENGTH)
|
||||
/* Cross pages read, read until offset 256 in low page */
|
||||
size -= offset + size - MLX5_EEPROM_PAGE_LENGTH;
|
||||
|
||||
i2c_addr = MLX5_I2C_ADDR_LOW;
|
||||
if (offset >= MLX5_EEPROM_PAGE_LENGTH) {
|
||||
i2c_addr = MLX5_I2C_ADDR_HIGH;
|
||||
offset -= MLX5_EEPROM_PAGE_LENGTH;
|
||||
}
|
||||
|
||||
MLX5_SET(mcia_reg, in, l, 0);
|
||||
MLX5_SET(mcia_reg, in, module, module_num);
|
||||
MLX5_SET(mcia_reg, in, i2c_device_address, i2c_addr);
|
||||
MLX5_SET(mcia_reg, in, page_number, 0);
|
||||
MLX5_SET(mcia_reg, in, device_address, offset);
|
||||
MLX5_SET(mcia_reg, in, size, size);
|
||||
|
||||
err = mlx5_core_access_reg(dev, in, sizeof(in), out,
|
||||
sizeof(out), MLX5_REG_MCIA, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
status = MLX5_GET(mcia_reg, out, status);
|
||||
if (status) {
|
||||
mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
|
||||
status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy(data, ptr, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mlx5_query_module_eeprom);
|
||||
|
||||
static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc,
|
||||
int pvlc_size, u8 local_port)
|
||||
{
|
||||
|
|
|
@ -113,9 +113,10 @@ enum {
|
|||
MLX5_REG_PELC = 0x500e,
|
||||
MLX5_REG_PVLC = 0x500f,
|
||||
MLX5_REG_PCMR = 0x5041,
|
||||
MLX5_REG_PMLP = 0, /* TBD */
|
||||
MLX5_REG_PMLP = 0x5002,
|
||||
MLX5_REG_NODE_DESC = 0x6001,
|
||||
MLX5_REG_HOST_ENDIANNESS = 0x7004,
|
||||
MLX5_REG_MCIA = 0x9014,
|
||||
MLX5_REG_MLCR = 0x902b,
|
||||
};
|
||||
|
||||
|
|
|
@ -40,6 +40,19 @@ enum mlx5_beacon_duration {
|
|||
MLX5_BEACON_DURATION_INF = 0xffff,
|
||||
};
|
||||
|
||||
enum mlx5_module_id {
|
||||
MLX5_MODULE_ID_SFP = 0x3,
|
||||
MLX5_MODULE_ID_QSFP = 0xC,
|
||||
MLX5_MODULE_ID_QSFP_PLUS = 0xD,
|
||||
MLX5_MODULE_ID_QSFP28 = 0x11,
|
||||
};
|
||||
|
||||
#define MLX5_EEPROM_MAX_BYTES 32
|
||||
#define MLX5_EEPROM_IDENTIFIER_BYTE_MASK 0x000000ff
|
||||
#define MLX5_I2C_ADDR_LOW 0x50
|
||||
#define MLX5_I2C_ADDR_HIGH 0x51
|
||||
#define MLX5_EEPROM_PAGE_LENGTH 256
|
||||
|
||||
int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps);
|
||||
int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys,
|
||||
int ptys_size, int proto_mask, u8 local_port);
|
||||
|
@ -93,5 +106,7 @@ int mlx5_query_port_wol(struct mlx5_core_dev *mdev, u8 *wol_mode);
|
|||
int mlx5_set_port_fcs(struct mlx5_core_dev *mdev, u8 enable);
|
||||
void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported,
|
||||
bool *enabled);
|
||||
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
|
||||
u16 offset, u16 size, u8 *data);
|
||||
|
||||
#endif /* __MLX5_PORT_H__ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче