diff mbox series

[RFC,net-next,8/8] mlxsw: Add ability to reset transceiver modules

Message ID 20210809102152.719961-9-idosch@idosch.org
State New
Headers show
Series ethtool: Add ability to control transceiver modules | expand

Commit Message

Ido Schimmel Aug. 9, 2021, 10:21 a.m. UTC
From: Ido Schimmel <idosch@nvidia.com>

Implement support for ethtool_ops::reset_module. This is done by writing
to the "rst" bit of the PMAOS register and waiting for the module to
reach a valid operational state. If the module does not transition to a
valid state, an error is reported to user space via extack.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
---
 .../net/ethernet/mellanox/mlxsw/core_env.c    | 28 +++++++++++++++++++
 .../net/ethernet/mellanox/mlxsw/core_env.h    |  3 ++
 drivers/net/ethernet/mellanox/mlxsw/minimal.c | 10 +++++++
 .../mellanox/mlxsw/spectrum_ethtool.c         | 19 +++++++++++++
 4 files changed, 60 insertions(+)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_env.c b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
index 1ae06730d374..df578ef3319c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_env.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.c
@@ -587,6 +587,34 @@  int mlxsw_env_set_module_low_power(struct mlxsw_core *mlxsw_core, u8 module,
 }
 EXPORT_SYMBOL(mlxsw_env_set_module_low_power);
 
+static int mlxsw_env_module_reset(struct mlxsw_core *mlxsw_core, u8 module)
+{
+	char pmaos_pl[MLXSW_REG_PMAOS_LEN];
+
+	mlxsw_reg_pmaos_pack(pmaos_pl, module);
+	mlxsw_reg_pmaos_rst_set(pmaos_pl, true);
+
+	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
+}
+
+int mlxsw_env_reset_module(struct mlxsw_core *mlxsw_core, u8 module,
+			   struct netlink_ext_ack *extack)
+{
+	int err;
+
+	err = mlxsw_env_module_reset(mlxsw_core, module);
+	if (err) {
+		NL_SET_ERR_MSG_MOD(extack, "Failed to reset module");
+		return err;
+	}
+
+	/* Wait for the module to reach a valid operational state following its
+	 * reset.
+	 */
+	return mlxsw_env_module_oper_wait(mlxsw_core, module, extack);
+}
+EXPORT_SYMBOL(mlxsw_env_reset_module);
+
 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 32960de96674..465a095e6a3e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_env.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_env.h
@@ -32,6 +32,9 @@  int mlxsw_env_set_module_low_power(struct mlxsw_core *mlxsw_core, u8 module,
 				   bool low_power,
 				   struct netlink_ext_ack *extack);
 
+int mlxsw_env_reset_module(struct mlxsw_core *mlxsw_core, u8 module,
+			   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 6fb8204c4d8a..d206442270df 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/minimal.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/minimal.c
@@ -146,6 +146,15 @@  static int mlxsw_m_set_module_low_power(struct net_device *netdev,
 					      low_power, extack);
 }
 
+static int mlxsw_m_reset_module(struct net_device *netdev,
+				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_reset_module(core, mlxsw_m_port->module, 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,
@@ -153,6 +162,7 @@  static const struct ethtool_ops mlxsw_m_port_ethtool_ops = {
 	.get_module_eeprom_by_page = mlxsw_m_get_module_eeprom_by_page,
 	.get_module_low_power	= mlxsw_m_get_module_low_power,
 	.set_module_low_power	= mlxsw_m_set_module_low_power,
+	.reset_module		= mlxsw_m_reset_module,
 };
 
 static int
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
index fb6256f16c50..9526ef71e513 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
@@ -1244,6 +1244,24 @@  static int mlxsw_sp_set_module_low_power(struct net_device *dev, bool low_power,
 					      extack);
 }
 
+static int mlxsw_sp_reset_module(struct net_device *dev,
+				 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;
+
+	/* We are going to take the module down, so no port using it can be
+	 * administratively up.
+	 */
+	if (mlxsw_sp_module_ports_up_check(mlxsw_sp, module)) {
+		NL_SET_ERR_MSG_MOD(extack, "Cannot reset module when ports using it are administratively up");
+		return -EINVAL;
+	}
+
+	return mlxsw_env_reset_module(mlxsw_sp->core, module, extack);
+}
+
 const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
 	.cap_link_lanes_supported	= true,
 	.get_drvinfo			= mlxsw_sp_port_get_drvinfo,
@@ -1267,6 +1285,7 @@  const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
 	.get_rmon_stats			= mlxsw_sp_get_rmon_stats,
 	.get_module_low_power		= mlxsw_sp_get_module_low_power,
 	.set_module_low_power		= mlxsw_sp_set_module_low_power,
+	.reset_module			= mlxsw_sp_reset_module,
 };
 
 struct mlxsw_sp1_port_link_mode {