Commit 5706383b authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mlxsw-Add-support-for-transceiver-modules-reset'

Ido Schimmel says:

====================
mlxsw: Add support for transceiver modules reset

This patchset prepares mlxsw for future transceiver modules related [1]
changes and adds reset support via the existing 'ETHTOOL_RESET'
interface.

Patches #1-#6 are relatively straightforward preparations.

Patch #7 tracks the number of logical ports that are mapped to the
transceiver module and the number of logical ports using it that are
administratively up. Needed for both reset support and power mode policy
support.

Patches #8-#9 add required fields in device registers.

Patch #10 implements support for ethtool_ops::reset in order to reset
transceiver modules.

[1] https://lore.kernel.org/netdev/20210824130344.1828076-1-idosch@idosch.org/


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 00135227 49fd3b64
Loading
Loading
Loading
Loading
+10 −19
Original line number Diff line number Diff line
@@ -90,7 +90,6 @@ struct mlxsw_core {
		struct devlink_health_reporter *fw_fatal;
	} health;
	struct mlxsw_env *env;
	bool is_initialized; /* Denotes if core was already initialized. */
	unsigned long driver_priv[];
	/* driver_priv has to be always the last item */
};
@@ -1995,12 +1994,6 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
	if (err)
		goto err_health_init;

	if (mlxsw_driver->init) {
		err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack);
		if (err)
			goto err_driver_init;
	}

	err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
	if (err)
		goto err_hwmon_init;
@@ -2014,7 +2007,12 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
	if (err)
		goto err_env_init;

	mlxsw_core->is_initialized = true;
	if (mlxsw_driver->init) {
		err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack);
		if (err)
			goto err_driver_init;
	}

	devlink_params_publish(devlink);

	if (!reload)
@@ -2022,14 +2020,13 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,

	return 0;

err_driver_init:
	mlxsw_env_fini(mlxsw_core->env);
err_env_init:
	mlxsw_thermal_fini(mlxsw_core->thermal);
err_thermal_init:
	mlxsw_hwmon_fini(mlxsw_core->hwmon);
err_hwmon_init:
	if (mlxsw_core->driver->fini)
		mlxsw_core->driver->fini(mlxsw_core);
err_driver_init:
	mlxsw_core_health_fini(mlxsw_core);
err_health_init:
err_fw_rev_validate:
@@ -2100,12 +2097,11 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
	}

	devlink_params_unpublish(devlink);
	mlxsw_core->is_initialized = false;
	if (mlxsw_core->driver->fini)
		mlxsw_core->driver->fini(mlxsw_core);
	mlxsw_env_fini(mlxsw_core->env);
	mlxsw_thermal_fini(mlxsw_core->thermal);
	mlxsw_hwmon_fini(mlxsw_core->hwmon);
	if (mlxsw_core->driver->fini)
		mlxsw_core->driver->fini(mlxsw_core);
	mlxsw_core_health_fini(mlxsw_core);
	if (!reload)
		mlxsw_core_params_unregister(mlxsw_core);
@@ -2939,11 +2935,6 @@ struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core)
	return mlxsw_core->env;
}

bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core)
{
	return mlxsw_core->is_initialized;
}

static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
				    const char *buf, size_t size)
{
+0 −1
Original line number Diff line number Diff line
@@ -249,7 +249,6 @@ mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core,
				 u8 local_port);
bool mlxsw_core_port_is_xm(const struct mlxsw_core *mlxsw_core, u8 local_port);
struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core);
bool mlxsw_core_is_initialized(const struct mlxsw_core *mlxsw_core);

int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay);
bool mlxsw_core_schedule_work(struct work_struct *work);
+160 −23
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/err.h>
#include <linux/ethtool.h>
#include <linux/sfp.h>
#include <linux/mutex.h>

#include "core.h"
#include "core_env.h"
@@ -14,12 +15,14 @@
struct mlxsw_env_module_info {
	u64 module_overheat_counter;
	bool is_overheat;
	int num_ports_mapped;
	int num_ports_up;
};

struct mlxsw_env {
	struct mlxsw_core *core;
	u8 module_count;
	spinlock_t module_info_lock; /* Protects 'module_info'. */
	struct mutex module_info_lock; /* Protects 'module_info'. */
	struct mlxsw_env_module_info module_info[];
};

@@ -389,6 +392,59 @@ mlxsw_env_get_module_eeprom_by_page(struct mlxsw_core *mlxsw_core, u8 module,
}
EXPORT_SYMBOL(mlxsw_env_get_module_eeprom_by_page);

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 net_device *netdev,
			   struct mlxsw_core *mlxsw_core, u8 module, u32 *flags)
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);
	u32 req = *flags;
	int err;

	if (!(req & ETH_RESET_PHY) &&
	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT)))
		return 0;

	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return -EINVAL;

	mutex_lock(&mlxsw_env->module_info_lock);

	if (mlxsw_env->module_info[module].num_ports_up) {
		netdev_err(netdev, "Cannot reset module when ports using it are administratively up\n");
		err = -EINVAL;
		goto out;
	}

	if (mlxsw_env->module_info[module].num_ports_mapped > 1 &&
	    !(req & (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT))) {
		netdev_err(netdev, "Cannot reset module without \"phy-shared\" flag when shared by multiple ports\n");
		err = -EINVAL;
		goto out;
	}

	err = mlxsw_env_module_reset(mlxsw_core, module);
	if (err) {
		netdev_err(netdev, "Failed to reset module\n");
		goto out;
	}

	*flags &= ~(ETH_RESET_PHY | (ETH_RESET_PHY << ETH_RESET_SHARED_SHIFT));

out:
	mutex_unlock(&mlxsw_env->module_info_lock);
	return err;
}
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)
@@ -482,22 +538,32 @@ static int mlxsw_env_module_temp_event_enable(struct mlxsw_core *mlxsw_core,
	return 0;
}

static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg,
				      char *mtwe_pl, void *priv)
struct mlxsw_env_module_temp_warn_event {
	struct mlxsw_env *mlxsw_env;
	char mtwe_pl[MLXSW_REG_MTWE_LEN];
	struct work_struct work;
};

static void mlxsw_env_mtwe_event_work(struct work_struct *work)
{
	struct mlxsw_env *mlxsw_env = priv;
	struct mlxsw_env_module_temp_warn_event *event;
	struct mlxsw_env *mlxsw_env;
	int i, sensor_warning;
	bool is_overheat;

	event = container_of(work, struct mlxsw_env_module_temp_warn_event,
			     work);
	mlxsw_env = event->mlxsw_env;

	for (i = 0; i < mlxsw_env->module_count; i++) {
		/* 64-127 of sensor_index are mapped to the port modules
		 * sequentially (module 0 is mapped to sensor_index 64,
		 * module 1 to sensor_index 65 and so on)
		 */
		sensor_warning =
			mlxsw_reg_mtwe_sensor_warning_get(mtwe_pl,
			mlxsw_reg_mtwe_sensor_warning_get(event->mtwe_pl,
							  i + MLXSW_REG_MTMP_MODULE_INDEX_MIN);
		spin_lock(&mlxsw_env->module_info_lock);
		mutex_lock(&mlxsw_env->module_info_lock);
		is_overheat =
			mlxsw_env->module_info[i].is_overheat;

@@ -507,13 +573,13 @@ static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg,
			 * warning OR current state in "no warning" and MTWE
			 * does not report warning.
			 */
			spin_unlock(&mlxsw_env->module_info_lock);
			mutex_unlock(&mlxsw_env->module_info_lock);
			continue;
		} else if (is_overheat && !sensor_warning) {
			/* MTWE reports "no warning", turn is_overheat off.
			 */
			mlxsw_env->module_info[i].is_overheat = false;
			spin_unlock(&mlxsw_env->module_info_lock);
			mutex_unlock(&mlxsw_env->module_info_lock);
		} else {
			/* Current state is "no warning" and MTWE reports
			 * "warning", increase the counter and turn is_overheat
@@ -521,13 +587,32 @@ static void mlxsw_env_mtwe_event_func(const struct mlxsw_reg_info *reg,
			 */
			mlxsw_env->module_info[i].is_overheat = true;
			mlxsw_env->module_info[i].module_overheat_counter++;
			spin_unlock(&mlxsw_env->module_info_lock);
			mutex_unlock(&mlxsw_env->module_info_lock);
		}
	}

	kfree(event);
}

static void
mlxsw_env_mtwe_listener_func(const struct mlxsw_reg_info *reg, char *mtwe_pl,
			     void *priv)
{
	struct mlxsw_env_module_temp_warn_event *event;
	struct mlxsw_env *mlxsw_env = priv;

	event = kmalloc(sizeof(*event), GFP_ATOMIC);
	if (!event)
		return;

	event->mlxsw_env = mlxsw_env;
	memcpy(event->mtwe_pl, mtwe_pl, MLXSW_REG_MTWE_LEN);
	INIT_WORK(&event->work, mlxsw_env_mtwe_event_work);
	mlxsw_core_schedule_work(&event->work);
}

static const struct mlxsw_listener mlxsw_env_temp_warn_listener =
	MLXSW_EVENTL(mlxsw_env_mtwe_event_func, MTWE, MTWE);
	MLXSW_EVENTL(mlxsw_env_mtwe_listener_func, MTWE, MTWE);

static int mlxsw_env_temp_warn_event_register(struct mlxsw_core *mlxsw_core)
{
@@ -568,9 +653,9 @@ static void mlxsw_env_pmpe_event_work(struct work_struct *work)
			     work);
	mlxsw_env = event->mlxsw_env;

	spin_lock_bh(&mlxsw_env->module_info_lock);
	mutex_lock(&mlxsw_env->module_info_lock);
	mlxsw_env->module_info[event->module].is_overheat = false;
	spin_unlock_bh(&mlxsw_env->module_info_lock);
	mutex_unlock(&mlxsw_env->module_info_lock);

	err = mlxsw_env_module_has_temp_sensor(mlxsw_env->core, event->module,
					       &has_temp_sensor);
@@ -652,8 +737,10 @@ mlxsw_env_module_oper_state_event_enable(struct mlxsw_core *mlxsw_core,
	for (i = 0; i < module_count; i++) {
		char pmaos_pl[MLXSW_REG_PMAOS_LEN];

		mlxsw_reg_pmaos_pack(pmaos_pl, i,
		mlxsw_reg_pmaos_pack(pmaos_pl, i);
		mlxsw_reg_pmaos_e_set(pmaos_pl,
				      MLXSW_REG_PMAOS_E_GENERATE_EVENT);
		mlxsw_reg_pmaos_ee_set(pmaos_pl, true);
		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(pmaos), pmaos_pl);
		if (err)
			return err;
@@ -667,22 +754,70 @@ mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);

	/* Prevent switch driver from accessing uninitialized data. */
	if (!mlxsw_core_is_initialized(mlxsw_core)) {
		*p_counter = 0;
	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return -EINVAL;

	mutex_lock(&mlxsw_env->module_info_lock);
	*p_counter = mlxsw_env->module_info[module].module_overheat_counter;
	mutex_unlock(&mlxsw_env->module_info_lock);

	return 0;
}
EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);

void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module)
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);

	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return;

	mutex_lock(&mlxsw_env->module_info_lock);
	mlxsw_env->module_info[module].num_ports_mapped++;
	mutex_unlock(&mlxsw_env->module_info_lock);
}
EXPORT_SYMBOL(mlxsw_env_module_port_map);

void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module)
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);

	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return;

	mutex_lock(&mlxsw_env->module_info_lock);
	mlxsw_env->module_info[module].num_ports_mapped--;
	mutex_unlock(&mlxsw_env->module_info_lock);
}
EXPORT_SYMBOL(mlxsw_env_module_port_unmap);

int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module)
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);

	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return -EINVAL;

	spin_lock_bh(&mlxsw_env->module_info_lock);
	*p_counter = mlxsw_env->module_info[module].module_overheat_counter;
	spin_unlock_bh(&mlxsw_env->module_info_lock);
	mutex_lock(&mlxsw_env->module_info_lock);
	mlxsw_env->module_info[module].num_ports_up++;
	mutex_unlock(&mlxsw_env->module_info_lock);

	return 0;
}
EXPORT_SYMBOL(mlxsw_env_module_overheat_counter_get);
EXPORT_SYMBOL(mlxsw_env_module_port_up);

void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module)
{
	struct mlxsw_env *mlxsw_env = mlxsw_core_env(mlxsw_core);

	if (WARN_ON_ONCE(module >= mlxsw_env->module_count))
		return;

	mutex_lock(&mlxsw_env->module_info_lock);
	mlxsw_env->module_info[module].num_ports_up--;
	mutex_unlock(&mlxsw_env->module_info_lock);
}
EXPORT_SYMBOL(mlxsw_env_module_port_down);

int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
{
@@ -702,7 +837,7 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
	if (!env)
		return -ENOMEM;

	spin_lock_init(&env->module_info_lock);
	mutex_init(&env->module_info_lock);
	env->core = mlxsw_core;
	env->module_count = module_count;
	*p_env = env;
@@ -732,6 +867,7 @@ int mlxsw_env_init(struct mlxsw_core *mlxsw_core, struct mlxsw_env **p_env)
err_module_plug_event_register:
	mlxsw_env_temp_warn_event_unregister(env);
err_temp_warn_event_register:
	mutex_destroy(&env->module_info_lock);
	kfree(env);
	return err;
}
@@ -742,5 +878,6 @@ void mlxsw_env_fini(struct mlxsw_env *env)
	/* Make sure there is no more event work scheduled. */
	mlxsw_core_flush_owq();
	mlxsw_env_temp_warn_event_unregister(env);
	mutex_destroy(&env->module_info_lock);
	kfree(env);
}
+13 −0
Original line number Diff line number Diff line
@@ -24,9 +24,22 @@ 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_reset_module(struct net_device *netdev,
			   struct mlxsw_core *mlxsw_core, u8 module,
			   u32 *flags);

int
mlxsw_env_module_overheat_counter_get(struct mlxsw_core *mlxsw_core, u8 module,
				      u64 *p_counter);

void mlxsw_env_module_port_map(struct mlxsw_core *mlxsw_core, u8 module);

void mlxsw_env_module_port_unmap(struct mlxsw_core *mlxsw_core, u8 module);

int mlxsw_env_module_port_up(struct mlxsw_core *mlxsw_core, u8 module);

void mlxsw_env_module_port_down(struct mlxsw_core *mlxsw_core, u8 module);

int mlxsw_env_init(struct mlxsw_core *core, struct mlxsw_env **p_env);
void mlxsw_env_fini(struct mlxsw_env *env);

+27 −3
Original line number Diff line number Diff line
@@ -54,8 +54,20 @@ static int mlxsw_m_base_mac_get(struct mlxsw_m *mlxsw_m)
	return 0;
}

static int mlxsw_m_port_dummy_open_stop(struct net_device *dev)
static int mlxsw_m_port_open(struct net_device *dev)
{
	struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
	struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;

	return mlxsw_env_module_port_up(mlxsw_m->core, mlxsw_m_port->module);
}

static int mlxsw_m_port_stop(struct net_device *dev)
{
	struct mlxsw_m_port *mlxsw_m_port = netdev_priv(dev);
	struct mlxsw_m *mlxsw_m = mlxsw_m_port->mlxsw_m;

	mlxsw_env_module_port_down(mlxsw_m->core, mlxsw_m_port->module);
	return 0;
}

@@ -70,8 +82,8 @@ mlxsw_m_port_get_devlink_port(struct net_device *dev)
}

static const struct net_device_ops mlxsw_m_port_netdev_ops = {
	.ndo_open		= mlxsw_m_port_dummy_open_stop,
	.ndo_stop		= mlxsw_m_port_dummy_open_stop,
	.ndo_open		= mlxsw_m_port_open,
	.ndo_stop		= mlxsw_m_port_stop,
	.ndo_get_devlink_port	= mlxsw_m_port_get_devlink_port,
};

@@ -124,11 +136,21 @@ mlxsw_m_get_module_eeprom_by_page(struct net_device *netdev,
						   page, extack);
}

static int mlxsw_m_reset(struct net_device *netdev, u32 *flags)
{
	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(netdev, core, mlxsw_m_port->module,
				      flags);
}

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,
	.reset			= mlxsw_m_reset,
};

static int
@@ -266,6 +288,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,

	if (WARN_ON_ONCE(module >= max_ports))
		return -EINVAL;
	mlxsw_env_module_port_map(mlxsw_m->core, module);
	mlxsw_m->module_to_port[module] = ++mlxsw_m->max_ports;

	return 0;
@@ -274,6 +297,7 @@ static int mlxsw_m_port_module_map(struct mlxsw_m *mlxsw_m, u8 local_port,
static void mlxsw_m_port_module_unmap(struct mlxsw_m *mlxsw_m, u8 module)
{
	mlxsw_m->module_to_port[module] = -1;
	mlxsw_env_module_port_unmap(mlxsw_m->core, module);
}

static int mlxsw_m_ports_create(struct mlxsw_m *mlxsw_m)
Loading