Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b5d331a authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mlxsw-Hardware-monitoring-enhancements'



Ido Schimmel says:

====================
mlxsw: Hardware monitoring enhancements

This patchset from Vadim provides various hardware monitoring related
improvements for mlxsw.

Patch #1 allows querying firmware version from the switch driver when
the underlying bus is I2C. This is useful for baseboard management
controller (BMC) systems that communicate with the ASIC over I2C.

Patch #2 improves driver's performance over I2C by utilizing larger
transactions sizes, if possible.

Patch #3 re-orders driver's initialization sequence to enforce a
specific firmware version before new firmware features are utilized.
This is a prerequisite for patches #4-#6.

Patches #4-#6 expose the temperature of inter-connect devices
(gearboxes) that are present in Mellanox SN3800 systems and split
2x50Gb/s lanes to 4x25Gb/s lanes.

Patches #7-#8 reduce the transaction size when reading SFP modules
temperatures, which is crucial when working over I2C.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 18a49727 e4e93d6d
Loading
Loading
Loading
Loading
+11 −10
Original line number Diff line number Diff line
@@ -1098,6 +1098,12 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
			goto err_register_params;
	}

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

	err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
	if (err)
		goto err_hwmon_init;
@@ -1107,22 +1113,17 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
	if (err)
		goto err_thermal_init;

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

	if (mlxsw_driver->params_register && !reload)
		devlink_params_publish(devlink);

	return 0;

err_driver_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:
	if (mlxsw_driver->params_unregister && !reload)
		mlxsw_driver->params_unregister(mlxsw_core);
err_register_params:
@@ -1187,10 +1188,10 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,

	if (mlxsw_core->driver->params_unregister && !reload)
		devlink_params_unpublish(devlink);
	if (mlxsw_core->driver->fini)
		mlxsw_core->driver->fini(mlxsw_core);
	mlxsw_thermal_fini(mlxsw_core->thermal);
	mlxsw_hwmon_fini(mlxsw_core->hwmon);
	if (mlxsw_core->driver->fini)
		mlxsw_core->driver->fini(mlxsw_core);
	if (mlxsw_core->driver->params_unregister && !reload)
		mlxsw_core->driver->params_unregister(mlxsw_core);
	if (!reload)
+7 −20
Original line number Diff line number Diff line
@@ -92,34 +92,21 @@ int mlxsw_env_module_temp_thresholds_get(struct mlxsw_core *core, int module,
		u16 temp;
	} temp_thresh;
	char mcia_pl[MLXSW_REG_MCIA_LEN] = {0};
	char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
	u16 module_temp;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned int module_temp;
	bool qsfp;
	int err;

	mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
			    1);
	err = mlxsw_reg_query(core, MLXSW_REG(mtbr), mtbr_pl);
	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
			    false, false);
	err = mlxsw_reg_query(core, MLXSW_REG(mtmp), mtmp_pl);
	if (err)
		return err;

	/* Don't read temperature thresholds for module with no valid info. */
	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &module_temp, NULL);
	switch (module_temp) {
	case MLXSW_REG_MTBR_BAD_SENS_INFO: /* fall-through */
	case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
	case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
	case MLXSW_REG_MTBR_INDEX_NA:
		*temp = 0;
		return 0;
	default:
		/* Do not consider thresholds for zero temperature. */
		if (MLXSW_REG_MTMP_TEMP_TO_MC(module_temp) == 0) {
	mlxsw_reg_mtmp_unpack(mtmp_pl, &module_temp, NULL, NULL);
	if (!module_temp) {
		*temp = 0;
		return 0;
	}
		break;
	}

	/* Read Free Side Device Temperature Thresholds from page 03h
	 * (MSB at lower byte address).
+103 −32
Original line number Diff line number Diff line
@@ -23,6 +23,14 @@ struct mlxsw_hwmon_attr {
	char name[32];
};

static int mlxsw_hwmon_get_attr_index(int index, int count)
{
	if (index >= count)
		return index % count + MLXSW_REG_MTMP_GBOX_INDEX_MIN;

	return index;
}

struct mlxsw_hwmon {
	struct mlxsw_core *core;
	const struct mlxsw_bus_info *bus_info;
@@ -33,6 +41,7 @@ struct mlxsw_hwmon {
	struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
	unsigned int attrs_count;
	u8 sensor_count;
	u8 module_sensor_count;
};

static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
@@ -44,10 +53,12 @@ static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned int temp;
	int index;
	int err;

	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
			    false, false);
	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
					   mlxsw_hwmon->module_sensor_count);
	mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false);
	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
	if (err) {
		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
@@ -66,10 +77,12 @@ static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned int temp_max;
	int index;
	int err;

	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
			    false, false);
	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
					   mlxsw_hwmon->module_sensor_count);
	mlxsw_reg_mtmp_pack(mtmp_pl, index, false, false);
	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
	if (err) {
		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
@@ -88,6 +101,7 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned long val;
	int index;
	int err;

	err = kstrtoul(buf, 10, &val);
@@ -96,7 +110,9 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
	if (val != 1)
		return -EINVAL;

	mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true);
	index = mlxsw_hwmon_get_attr_index(mlwsw_hwmon_attr->type_index,
					   mlxsw_hwmon->module_sensor_count);
	mlxsw_reg_mtmp_pack(mtmp_pl, index, true, true);
	err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
	if (err) {
		dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
@@ -198,38 +214,18 @@ static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev,
	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
	char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0};
	u16 temp;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned int temp;
	u8 module;
	int err;

	module = mlwsw_hwmon_attr->type_index - mlxsw_hwmon->sensor_count;
	mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX + module,
			    1);
	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtbr), mtbr_pl);
	if (err) {
		dev_err(dev, "Failed to query module temperature sensor\n");
	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN + module,
			    false, false);
	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
	if (err)
		return err;
	}

	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
	/* Update status and temperature cache. */
	switch (temp) {
	case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
	case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
	case MLXSW_REG_MTBR_INDEX_NA:
		temp = 0;
		break;
	case MLXSW_REG_MTBR_BAD_SENS_INFO:
		/* Untrusted cable is connected. Reading temperature from its
		 * sensor is faulty.
		 */
		temp = 0;
		break;
	default:
		temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
		break;
	}
	mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);

	return sprintf(buf, "%u\n", temp);
}
@@ -333,6 +329,20 @@ mlxsw_hwmon_module_temp_label_show(struct device *dev,
		       mlwsw_hwmon_attr->type_index);
}

static ssize_t
mlxsw_hwmon_gbox_temp_label_show(struct device *dev,
				 struct device_attribute *attr,
				 char *buf)
{
	struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
			container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
	struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
	int index = mlwsw_hwmon_attr->type_index -
		    mlxsw_hwmon->module_sensor_count + 1;

	return sprintf(buf, "gearbox %03u\n", index);
}

enum mlxsw_hwmon_attr_type {
	MLXSW_HWMON_ATTR_TYPE_TEMP,
	MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
@@ -345,6 +355,7 @@ enum mlxsw_hwmon_attr_type {
	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT,
	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG,
	MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL,
	MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,
};

static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
@@ -428,6 +439,13 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
			 "temp%u_label", num + 1);
		break;
	case MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL:
		mlxsw_hwmon_attr->dev_attr.show =
			mlxsw_hwmon_gbox_temp_label_show;
		mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
		snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
			 "temp%u_label", num + 1);
		break;
	default:
		WARN_ON(1);
	}
@@ -556,6 +574,54 @@ static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
				     index, index);
		index++;
	}
	mlxsw_hwmon->module_sensor_count = index;

	return 0;
}

static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon *mlxsw_hwmon)
{
	int index, max_index, sensor_index;
	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	u8 gbox_num;
	int err;

	mlxsw_reg_mgpir_pack(mgpir_pl);
	err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mgpir), mgpir_pl);
	if (err)
		return err;

	mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, NULL, NULL);
	if (!gbox_num)
		return 0;

	index = mlxsw_hwmon->module_sensor_count;
	max_index = mlxsw_hwmon->module_sensor_count + gbox_num;
	while (index < max_index) {
		sensor_index = index % mlxsw_hwmon->module_sensor_count +
			       MLXSW_REG_MTMP_GBOX_INDEX_MIN;
		mlxsw_reg_mtmp_pack(mtmp_pl, sensor_index, true, true);
		err = mlxsw_reg_write(mlxsw_hwmon->core,
				      MLXSW_REG(mtmp), mtmp_pl);
		if (err) {
			dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
				sensor_index);
			return err;
		}
		mlxsw_hwmon_attr_add(mlxsw_hwmon, MLXSW_HWMON_ATTR_TYPE_TEMP,
				     index, index);
		mlxsw_hwmon_attr_add(mlxsw_hwmon,
				     MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, index,
				     index);
		mlxsw_hwmon_attr_add(mlxsw_hwmon,
				     MLXSW_HWMON_ATTR_TYPE_TEMP_RST, index,
				     index);
		mlxsw_hwmon_attr_add(mlxsw_hwmon,
				     MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL,
				     index, index);
		index++;
	}

	return 0;
}
@@ -586,6 +652,10 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
	if (err)
		goto err_temp_module_init;

	err = mlxsw_hwmon_gearbox_init(mlxsw_hwmon);
	if (err)
		goto err_temp_gearbox_init;

	mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
	mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;

@@ -602,6 +672,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
	return 0;

err_hwmon_register:
err_temp_gearbox_init:
err_temp_module_init:
err_fans_init:
err_temp_init:
+19 −27
Original line number Diff line number Diff line
@@ -449,39 +449,31 @@ static int mlxsw_thermal_module_temp_get(struct thermal_zone_device *tzdev,
	struct mlxsw_thermal_module *tz = tzdev->devdata;
	struct mlxsw_thermal *thermal = tz->parent;
	struct device *dev = thermal->bus_info->dev;
	char mtbr_pl[MLXSW_REG_MTBR_LEN];
	u16 temp;
	char mtmp_pl[MLXSW_REG_MTMP_LEN];
	unsigned int temp;
	int err;

	/* Read module temperature. */
	mlxsw_reg_mtbr_pack(mtbr_pl, MLXSW_REG_MTBR_BASE_MODULE_INDEX +
			    tz->module, 1);
	err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtbr), mtbr_pl);
	if (err)
		return err;

	mlxsw_reg_mtbr_temp_unpack(mtbr_pl, 0, &temp, NULL);
	/* Update temperature. */
	switch (temp) {
	case MLXSW_REG_MTBR_NO_CONN: /* fall-through */
	case MLXSW_REG_MTBR_NO_TEMP_SENS: /* fall-through */
	case MLXSW_REG_MTBR_INDEX_NA: /* fall-through */
	case MLXSW_REG_MTBR_BAD_SENS_INFO:
	mlxsw_reg_mtmp_pack(mtmp_pl, MLXSW_REG_MTMP_MODULE_INDEX_MIN +
			    tz->module, false, false);
	err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtmp), mtmp_pl);
	if (err) {
		/* Do not return error - in case of broken module's sensor
		 * it will cause error message flooding.
		 */
		temp = 0;
		break;
	default:
		temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
		/* Reset all trip point. */
		mlxsw_thermal_module_trips_reset(tz);
		/* Update trip points. */
		err = mlxsw_thermal_module_trips_update(dev, thermal->core,
							tz);
		if (err)
			return err;
		break;
		*p_temp = (int) temp;
		return 0;
	}

	mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
	*p_temp = (int) temp;

	if (!temp)
		return 0;

	/* Update trip points. */
	mlxsw_thermal_module_trips_update(dev, thermal->core, tz);

	return 0;
}

+58 −18
Original line number Diff line number Diff line
@@ -43,11 +43,10 @@
#define MLXSW_I2C_PREP_SIZE		(MLXSW_I2C_ADDR_WIDTH + 28)
#define MLXSW_I2C_MBOX_SIZE		20
#define MLXSW_I2C_MBOX_OUT_PARAM_OFF	12
#define MLXSW_I2C_MAX_BUFF_SIZE		32
#define MLXSW_I2C_MBOX_OFFSET_BITS	20
#define MLXSW_I2C_MBOX_SIZE_BITS	12
#define MLXSW_I2C_ADDR_BUF_SIZE		4
#define MLXSW_I2C_BLK_MAX		32
#define MLXSW_I2C_BLK_DEF		32
#define MLXSW_I2C_RETRY			5
#define MLXSW_I2C_TIMEOUT_MSECS		5000
#define MLXSW_I2C_MAX_DATA_SIZE		256
@@ -62,6 +61,7 @@
 * @dev: I2C device;
 * @core: switch core pointer;
 * @bus_info: bus info block;
 * @block_size: maximum block size allowed to pass to under layer;
 */
struct mlxsw_i2c {
	struct {
@@ -74,6 +74,7 @@ struct mlxsw_i2c {
	struct device *dev;
	struct mlxsw_core *core;
	struct mlxsw_bus_info bus_info;
	u16 block_size;
};

#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) {	\
@@ -315,20 +316,26 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
	struct i2c_client *client = to_i2c_client(dev);
	struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
	unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS);
	u8 tran_buf[MLXSW_I2C_MAX_BUFF_SIZE + MLXSW_I2C_ADDR_BUF_SIZE];
	int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j;
	unsigned long end;
	u8 *tran_buf;
	struct i2c_msg write_tran =
		MLXSW_I2C_WRITE_MSG(client, tran_buf, MLXSW_I2C_PUSH_CMD_SIZE);
		MLXSW_I2C_WRITE_MSG(client, NULL, MLXSW_I2C_PUSH_CMD_SIZE);
	int err;

	tran_buf = kmalloc(mlxsw_i2c->block_size + MLXSW_I2C_ADDR_BUF_SIZE,
			   GFP_KERNEL);
	if (!tran_buf)
		return -ENOMEM;

	write_tran.buf = tran_buf;
	for (i = 0; i < num; i++) {
		chunk_size = (in_mbox_size > MLXSW_I2C_BLK_MAX) ?
			     MLXSW_I2C_BLK_MAX : in_mbox_size;
		chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ?
			     mlxsw_i2c->block_size : in_mbox_size;
		write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size;
		mlxsw_i2c_set_slave_addr(tran_buf, off);
		memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox +
		       MLXSW_I2C_BLK_MAX * i, chunk_size);
		       mlxsw_i2c->block_size * i, chunk_size);

		j = 0;
		end = jiffies + timeout;
@@ -342,9 +349,10 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
			 (j++ < MLXSW_I2C_RETRY));

		if (err != 1) {
			if (!err)
			if (!err) {
				err = -EIO;
			return err;
				goto mlxsw_i2c_write_exit;
			}
		}

		off += chunk_size;
@@ -355,24 +363,27 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
	err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0);
	if (err) {
		dev_err(&client->dev, "Could not start transaction");
		return -EIO;
		err = -EIO;
		goto mlxsw_i2c_write_exit;
	}

	/* Wait until go bit is cleared. */
	err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status);
	if (err) {
		dev_err(&client->dev, "HW semaphore is not released");
		return err;
		goto mlxsw_i2c_write_exit;
	}

	/* Validate transaction completion status. */
	if (*p_status) {
		dev_err(&client->dev, "Bad transaction completion status %x\n",
			*p_status);
		return -EIO;
		err = -EIO;
	}

	return 0;
mlxsw_i2c_write_exit:
	kfree(tran_buf);
	return err;
}

/* Routine executes I2C command. */
@@ -395,8 +406,8 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,

	if (in_mbox) {
		reg_size = mlxsw_i2c_get_reg_size(in_mbox);
		num = reg_size / MLXSW_I2C_BLK_MAX;
		if (reg_size % MLXSW_I2C_BLK_MAX)
		num = reg_size / mlxsw_i2c->block_size;
		if (reg_size % mlxsw_i2c->block_size)
			num++;

		if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
@@ -416,7 +427,7 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
	} else {
		/* No input mailbox is case of initialization query command. */
		reg_size = MLXSW_I2C_MAX_DATA_SIZE;
		num = reg_size / MLXSW_I2C_BLK_MAX;
		num = reg_size / mlxsw_i2c->block_size;

		if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
			dev_err(&client->dev, "Could not acquire lock");
@@ -432,8 +443,8 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
	/* Send read transaction to get output mailbox content. */
	read_tran[1].buf = out_mbox;
	for (i = 0; i < num; i++) {
		chunk_size = (reg_size > MLXSW_I2C_BLK_MAX) ?
			     MLXSW_I2C_BLK_MAX : reg_size;
		chunk_size = (reg_size > mlxsw_i2c->block_size) ?
			     mlxsw_i2c->block_size : reg_size;
		read_tran[1].len = chunk_size;
		mlxsw_i2c_set_slave_addr(tran_buf, off);

@@ -509,8 +520,20 @@ mlxsw_i2c_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
	if (!mbox)
		return -ENOMEM;

	err = mlxsw_cmd_query_fw(mlxsw_core, mbox);
	if (err)
		goto mbox_put;

	mlxsw_i2c->bus_info.fw_rev.major =
		mlxsw_cmd_mbox_query_fw_fw_rev_major_get(mbox);
	mlxsw_i2c->bus_info.fw_rev.minor =
		mlxsw_cmd_mbox_query_fw_fw_rev_minor_get(mbox);
	mlxsw_i2c->bus_info.fw_rev.subminor =
		mlxsw_cmd_mbox_query_fw_fw_rev_subminor_get(mbox);

	err = mlxsw_core_resources_query(mlxsw_core, mbox, res);

mbox_put:
	mlxsw_cmd_mbox_free(mbox);
	return err;
}
@@ -534,6 +557,7 @@ static const struct mlxsw_bus mlxsw_i2c_bus = {
static int mlxsw_i2c_probe(struct i2c_client *client,
			   const struct i2c_device_id *id)
{
	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
	struct mlxsw_i2c *mlxsw_i2c;
	u8 status;
	int err;
@@ -542,6 +566,22 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
	if (!mlxsw_i2c)
		return -ENOMEM;

	if (quirks) {
		if ((quirks->max_read_len &&
		     quirks->max_read_len < MLXSW_I2C_BLK_DEF) ||
		    (quirks->max_write_len &&
		     quirks->max_write_len < MLXSW_I2C_BLK_DEF)) {
			dev_err(&client->dev, "Insufficient transaction buffer length\n");
			return -EOPNOTSUPP;
		}

		mlxsw_i2c->block_size = max_t(u16, MLXSW_I2C_BLK_DEF,
					      min_t(u16, quirks->max_read_len,
						    quirks->max_write_len));
	} else {
		mlxsw_i2c->block_size = MLXSW_I2C_BLK_DEF;
	}

	i2c_set_clientdata(client, mlxsw_i2c);
	mutex_init(&mlxsw_i2c->cmd.lock);

Loading