From d51ea60e01f9fab3269e18d46657a9ae0c2fa3ad Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 21 Jun 2021 10:50:39 +0300 Subject: [PATCH 1/3] mlxsw: reg: Add bank number to MCIA register Add bank number to MCIA (Management Cable Info Access) register in order to allow access to banked pages on EEPROMs using CMIS (Common Management Interface Specification) memory map. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 93f1db3927af..cd60a0f91933 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -9714,6 +9714,12 @@ MLXSW_ITEM32(reg, mcia, page_number, 0x04, 16, 8); */ MLXSW_ITEM32(reg, mcia, device_address, 0x04, 0, 16); +/* reg_mcia_bank_number + * Bank number. + * Access: Index + */ +MLXSW_ITEM32(reg, mcia, bank_number, 0x08, 16, 8); + /* reg_mcia_size * Number of bytes to read/write (up to 48 bytes). * Access: RW From cecefb3a6eeb5d835abd051e67e27e1506289ccf Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 21 Jun 2021 10:50:40 +0300 Subject: [PATCH 2/3] mlxsw: reg: Document possible MCIA status values Will be used to emit meaningful messages to user space via extack in a subsequent patch. Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index cd60a0f91933..6fbda6ebd590 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -9690,6 +9690,20 @@ MLXSW_ITEM32(reg, mcia, l, 0x00, 31, 1); */ MLXSW_ITEM32(reg, mcia, module, 0x00, 16, 8); +enum { + MLXSW_REG_MCIA_STATUS_GOOD = 0, + /* No response from module's EEPROM. */ + MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE = 1, + /* Module type not supported by the device. */ + MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED = 2, + /* No module present indication. */ + MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED = 3, + /* Error occurred while trying to access module's EEPROM using I2C. */ + MLXSW_REG_MCIA_STATUS_I2C_ERROR = 9, + /* Module is disabled. */ + MLXSW_REG_MCIA_STATUS_MODULE_DISABLED = 16, +}; + /* reg_mcia_status * Module status. * Access: RO From 1e27b9e408039af6210708f76c6fe735c2c2c9c1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 21 Jun 2021 10:50:41 +0300 Subject: [PATCH 3/3] mlxsw: core: Add support for module EEPROM read by page Add support for ethtool_ops::get_module_eeprom_by_page() which allows user space to read transceiver module EEPROM based on passed parameters. The I2C address is not validated in order to avoid module-specific code. In case of wrong address, error will be returned from device's firmware. Tested by comparing output with legacy method (ioctl) output. Signed-off-by: Ido Schimmel Tested-by: Vadim Pasternak Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/core_env.c | 74 +++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/core_env.h | 7 ++ drivers/net/ethernet/mellanox/mlxsw/minimal.c | 13 ++++ .../mellanox/mlxsw/spectrum_ethtool.c | 14 ++++ 4 files changed, 108 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c index b3ca5bd33a7f..4a0dbdb6730b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c @@ -3,6 +3,7 @@ #include #include +#include #include #include "core.h" @@ -315,6 +316,79 @@ int mlxsw_env_get_module_eeprom(struct net_device *netdev, } EXPORT_SYMBOL(mlxsw_env_get_module_eeprom); +static int mlxsw_env_mcia_status_process(const char *mcia_pl, + struct netlink_ext_ack *extack) +{ + u8 status = mlxsw_reg_mcia_status_get(mcia_pl); + + switch (status) { + case MLXSW_REG_MCIA_STATUS_GOOD: + return 0; + case MLXSW_REG_MCIA_STATUS_NO_EEPROM_MODULE: + NL_SET_ERR_MSG_MOD(extack, "No response from module's EEPROM"); + return -EIO; + case MLXSW_REG_MCIA_STATUS_MODULE_NOT_SUPPORTED: + NL_SET_ERR_MSG_MOD(extack, "Module type not supported by the device"); + return -EOPNOTSUPP; + case MLXSW_REG_MCIA_STATUS_MODULE_NOT_CONNECTED: + NL_SET_ERR_MSG_MOD(extack, "No module present indication"); + return -EIO; + case MLXSW_REG_MCIA_STATUS_I2C_ERROR: + NL_SET_ERR_MSG_MOD(extack, "Error occurred while trying to access module's EEPROM using I2C"); + return -EIO; + case MLXSW_REG_MCIA_STATUS_MODULE_DISABLED: + NL_SET_ERR_MSG_MOD(extack, "Module is disabled"); + return -EIO; + default: + NL_SET_ERR_MSG_MOD(extack, "Unknown error"); + return -EIO; + } +} + +int +mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack) +{ + u32 bytes_read = 0; + u16 device_addr; + + /* Offset cannot be larger than 2 * ETH_MODULE_EEPROM_PAGE_LEN */ + device_addr = page->offset; + + while (bytes_read < page->length) { + char eeprom_tmp[MLXSW_REG_MCIA_EEPROM_SIZE]; + char mcia_pl[MLXSW_REG_MCIA_LEN]; + u8 size; + int err; + + size = min_t(u8, page->length - bytes_read, + MLXSW_REG_MCIA_EEPROM_SIZE); + + mlxsw_reg_mcia_pack(mcia_pl, module, 0, page->page, + device_addr + bytes_read, size, + page->i2c_address); + mlxsw_reg_mcia_bank_number_set(mcia_pl, page->bank); + + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcia), mcia_pl); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to access module's EEPROM"); + return err; + } + + err = mlxsw_env_mcia_status_process(mcia_pl, extack); + if (err) + return err; + + mlxsw_reg_mcia_eeprom_memcpy_from(mcia_pl, eeprom_tmp); + memcpy(page->data + bytes_read, eeprom_tmp, size); + bytes_read += size; + } + + return bytes_read; +} +EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page); + static int mlxsw_env_module_has_temp_sensor(struct mlxsw_core *mlxsw_core, u8 module, bool *p_has_temp_sensor) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.h b/drivers/net/ethernet/mellanox/mlxsw/core_env.h index 2b23f8a87862..0bf5bd0f8a7e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h @@ -4,6 +4,8 @@ #ifndef _MLXSW_CORE_ENV_H #define _MLXSW_CORE_ENV_H +#include + struct ethtool_modinfo; struct ethtool_eeprom; @@ -17,6 +19,11 @@ int mlxsw_env_get_module_eeprom(struct net_device *netdev, struct mlxsw_core *mlxsw_core, int module, struct ethtool_eeprom *ee, u8 *data); +int +mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack); + int mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module, u64 *p_counter); diff --git a/drivers/net/ethernet/mellanox/mlxsw/minimal.c b/drivers/net/ethernet/mellanox/mlxsw/minimal.c index 68102726c6a7..d9d56c44e994 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c +++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c @@ -112,10 +112,23 @@ mlxsw_m_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, ee, data); } +static int +mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack) +{ + struct mlxsw_m_port *mlxsw_m_port = netdev_priv(netdev); + struct mlxsw_core *core = mlxsw_m_port->mlxsw_m->core; + + return mlxsw_env_get_module_eeprom_by_page(core, mlxsw_m_port->module, + page, extack); +} + static const struct ethtool_ops mlxsw_m_port_ethtool_ops = { .get_drvinfo = mlxsw_m_module_get_drvinfo, .get_module_info = mlxsw_m_get_module_info, .get_module_eeprom = mlxsw_m_get_module_eeprom, + .get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page, }; static int diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index c8061beed6db..267590a0eee7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1050,6 +1050,19 @@ static int mlxsw_sp_get_module_eeprom(struct net_device *netdev, return err; } +static int +mlxsw_sp_get_module_eeprom_by_page(struct net_device *dev, + const struct ethtool_module_eeprom *page, + struct netlink_ext_ack *extack) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u8 module = mlxsw_sp_port->mapping.module; + + return mlxsw_env_get_module_eeprom_by_page(mlxsw_sp->core, module, page, + extack); +} + static int mlxsw_sp_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info) { @@ -1199,6 +1212,7 @@ const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .set_link_ksettings = mlxsw_sp_port_set_link_ksettings, .get_module_info = mlxsw_sp_get_module_info, .get_module_eeprom = mlxsw_sp_get_module_eeprom, + .get_module_eeprom_by_page = mlxsw_sp_get_module_eeprom_by_page, .get_ts_info = mlxsw_sp_get_ts_info, .get_eth_phy_stats = mlxsw_sp_get_eth_phy_stats, .get_eth_mac_stats = mlxsw_sp_get_eth_mac_stats,