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: 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>
This commit is contained in:
Vadim Pasternak 2019-05-29 11:47:16 +03:00 коммит произвёл David S. Miller
Родитель 9bbd7efbc0
Коммит 3029a693be
1 изменённых файлов: 46 добавлений и 18 удалений

Просмотреть файл

@ -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);
@ -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;
@ -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);