Skip to content

Commit

Permalink
mlxsw: i2c: Allow flexible setting of I2C transactions size
Browse files Browse the repository at this point in the history
Current implementation uses fixed size of I2C data transaction buffer.
Allow to set size of I2C transactions according to I2C physical adapter
capability. For that purpose adapter read and write size is obtained
from the I2C physical adapter and buffer size is set according to the
minimum of these two values. If adapter does not provide such info,
default buffer size is to be used.
It allows to improve performance of I2C access to silicon when long
size transactions are used.

Signed-off-by: Vadim Pasternak <vadimp@mellanox.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
mellanoxbmc authored and davem330 committed May 30, 2019
1 parent 9bbd7ef commit 3029a69
Showing 1 changed file with 46 additions and 18 deletions.
64 changes: 46 additions & 18 deletions drivers/net/ethernet/mellanox/mlxsw/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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) { \
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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. */
Expand All @@ -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) {
Expand All @@ -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");
Expand All @@ -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);

Expand Down Expand Up @@ -546,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;
Expand All @@ -554,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);

Expand Down

0 comments on commit 3029a69

Please sign in to comment.