Commit 3029a693 authored by Vadim Pasternak's avatar Vadim Pasternak Committed by David S. Miller

mlxsw: i2c: Allow flexible setting of I2C transactions size

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: default avatarVadim Pasternak <vadimp@mellanox.com>
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9bbd7efb
...@@ -43,11 +43,10 @@ ...@@ -43,11 +43,10 @@
#define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28) #define MLXSW_I2C_PREP_SIZE (MLXSW_I2C_ADDR_WIDTH + 28)
#define MLXSW_I2C_MBOX_SIZE 20 #define MLXSW_I2C_MBOX_SIZE 20
#define MLXSW_I2C_MBOX_OUT_PARAM_OFF 12 #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_OFFSET_BITS 20
#define MLXSW_I2C_MBOX_SIZE_BITS 12 #define MLXSW_I2C_MBOX_SIZE_BITS 12
#define MLXSW_I2C_ADDR_BUF_SIZE 4 #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_RETRY 5
#define MLXSW_I2C_TIMEOUT_MSECS 5000 #define MLXSW_I2C_TIMEOUT_MSECS 5000
#define MLXSW_I2C_MAX_DATA_SIZE 256 #define MLXSW_I2C_MAX_DATA_SIZE 256
...@@ -62,6 +61,7 @@ ...@@ -62,6 +61,7 @@
* @dev: I2C device; * @dev: I2C device;
* @core: switch core pointer; * @core: switch core pointer;
* @bus_info: bus info block; * @bus_info: bus info block;
* @block_size: maximum block size allowed to pass to under layer;
*/ */
struct mlxsw_i2c { struct mlxsw_i2c {
struct { struct {
...@@ -74,6 +74,7 @@ struct mlxsw_i2c { ...@@ -74,6 +74,7 @@ struct mlxsw_i2c {
struct device *dev; struct device *dev;
struct mlxsw_core *core; struct mlxsw_core *core;
struct mlxsw_bus_info bus_info; struct mlxsw_bus_info bus_info;
u16 block_size;
}; };
#define MLXSW_I2C_READ_MSG(_client, _addr_buf, _buf, _len) { \ #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, ...@@ -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 i2c_client *client = to_i2c_client(dev);
struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client); struct mlxsw_i2c *mlxsw_i2c = i2c_get_clientdata(client);
unsigned long timeout = msecs_to_jiffies(MLXSW_I2C_TIMEOUT_MSECS); 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; int off = mlxsw_i2c->cmd.mb_off_in, chunk_size, i, j;
unsigned long end; unsigned long end;
u8 *tran_buf;
struct i2c_msg write_tran = 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; 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++) { for (i = 0; i < num; i++) {
chunk_size = (in_mbox_size > MLXSW_I2C_BLK_MAX) ? chunk_size = (in_mbox_size > mlxsw_i2c->block_size) ?
MLXSW_I2C_BLK_MAX : in_mbox_size; mlxsw_i2c->block_size : in_mbox_size;
write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size; write_tran.len = MLXSW_I2C_ADDR_WIDTH + chunk_size;
mlxsw_i2c_set_slave_addr(tran_buf, off); mlxsw_i2c_set_slave_addr(tran_buf, off);
memcpy(&tran_buf[MLXSW_I2C_ADDR_BUF_SIZE], in_mbox + 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; j = 0;
end = jiffies + timeout; end = jiffies + timeout;
...@@ -342,9 +349,10 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, ...@@ -342,9 +349,10 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num,
(j++ < MLXSW_I2C_RETRY)); (j++ < MLXSW_I2C_RETRY));
if (err != 1) { if (err != 1) {
if (!err) if (!err) {
err = -EIO; err = -EIO;
return err; goto mlxsw_i2c_write_exit;
}
} }
off += chunk_size; off += chunk_size;
...@@ -355,24 +363,27 @@ mlxsw_i2c_write(struct device *dev, size_t in_mbox_size, u8 *in_mbox, int num, ...@@ -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); err = mlxsw_i2c_write_cmd(client, mlxsw_i2c, 0);
if (err) { if (err) {
dev_err(&client->dev, "Could not start transaction"); dev_err(&client->dev, "Could not start transaction");
return -EIO; err = -EIO;
goto mlxsw_i2c_write_exit;
} }
/* Wait until go bit is cleared. */ /* Wait until go bit is cleared. */
err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status); err = mlxsw_i2c_wait_go_bit(client, mlxsw_i2c, p_status);
if (err) { if (err) {
dev_err(&client->dev, "HW semaphore is not released"); dev_err(&client->dev, "HW semaphore is not released");
return err; goto mlxsw_i2c_write_exit;
} }
/* Validate transaction completion status. */ /* Validate transaction completion status. */
if (*p_status) { if (*p_status) {
dev_err(&client->dev, "Bad transaction completion status %x\n", dev_err(&client->dev, "Bad transaction completion status %x\n",
*p_status); *p_status);
return -EIO; err = -EIO;
} }
return 0; mlxsw_i2c_write_exit:
kfree(tran_buf);
return err;
} }
/* Routine executes I2C command. */ /* Routine executes I2C command. */
...@@ -395,8 +406,8 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size, ...@@ -395,8 +406,8 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
if (in_mbox) { if (in_mbox) {
reg_size = mlxsw_i2c_get_reg_size(in_mbox); reg_size = mlxsw_i2c_get_reg_size(in_mbox);
num = reg_size / MLXSW_I2C_BLK_MAX; num = reg_size / mlxsw_i2c->block_size;
if (reg_size % MLXSW_I2C_BLK_MAX) if (reg_size % mlxsw_i2c->block_size)
num++; num++;
if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) { 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, ...@@ -416,7 +427,7 @@ mlxsw_i2c_cmd(struct device *dev, u16 opcode, u32 in_mod, size_t in_mbox_size,
} else { } else {
/* No input mailbox is case of initialization query command. */ /* No input mailbox is case of initialization query command. */
reg_size = MLXSW_I2C_MAX_DATA_SIZE; 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) { if (mutex_lock_interruptible(&mlxsw_i2c->cmd.lock) < 0) {
dev_err(&client->dev, "Could not acquire lock"); 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, ...@@ -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. */ /* Send read transaction to get output mailbox content. */
read_tran[1].buf = out_mbox; read_tran[1].buf = out_mbox;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
chunk_size = (reg_size > MLXSW_I2C_BLK_MAX) ? chunk_size = (reg_size > mlxsw_i2c->block_size) ?
MLXSW_I2C_BLK_MAX : reg_size; mlxsw_i2c->block_size : reg_size;
read_tran[1].len = chunk_size; read_tran[1].len = chunk_size;
mlxsw_i2c_set_slave_addr(tran_buf, off); mlxsw_i2c_set_slave_addr(tran_buf, off);
...@@ -546,6 +557,7 @@ static const struct mlxsw_bus mlxsw_i2c_bus = { ...@@ -546,6 +557,7 @@ static const struct mlxsw_bus mlxsw_i2c_bus = {
static int mlxsw_i2c_probe(struct i2c_client *client, static int mlxsw_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
struct mlxsw_i2c *mlxsw_i2c; struct mlxsw_i2c *mlxsw_i2c;
u8 status; u8 status;
int err; int err;
...@@ -554,6 +566,22 @@ static int mlxsw_i2c_probe(struct i2c_client *client, ...@@ -554,6 +566,22 @@ static int mlxsw_i2c_probe(struct i2c_client *client,
if (!mlxsw_i2c) if (!mlxsw_i2c)
return -ENOMEM; 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); i2c_set_clientdata(client, mlxsw_i2c);
mutex_init(&mlxsw_i2c->cmd.lock); mutex_init(&mlxsw_i2c->cmd.lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment