Skip to content

Commit

Permalink
Merge branch 'mlxsw-Add-support-for-transceiver-modules-reset'
Browse files Browse the repository at this point in the history
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/[email protected]/
====================

Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
davem330 committed Sep 15, 2021
2 parents 0013522 + 49fd3b6 commit 5706383
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 58 deletions.
29 changes: 10 additions & 19 deletions drivers/net/ethernet/mellanox/mlxsw/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};
Expand Down Expand Up @@ -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;
Expand All @@ -2014,22 +2007,26 @@ __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)
devlink_reload_enable(devlink);

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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 0 additions & 1 deletion drivers/net/ethernet/mellanox/mlxsw/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
183 changes: 160 additions & 23 deletions drivers/net/ethernet/mellanox/mlxsw/core_env.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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[];
};

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -507,27 +573,46 @@ 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
* on.
*/
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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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_E_GENERATE_EVENT);
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;
Expand All @@ -667,23 +754,71 @@ 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;
return 0;
}

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

spin_lock_bh(&mlxsw_env->module_info_lock);
mutex_lock(&mlxsw_env->module_info_lock);
*p_counter = mlxsw_env->module_info[module].module_overheat_counter;
spin_unlock_bh(&mlxsw_env->module_info_lock);
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;

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_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)
{
char mgpir_pl[MLXSW_REG_MGPIR_LEN];
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
Loading

0 comments on commit 5706383

Please sign in to comment.