Commit d2b72f64 authored by Antti Palosaari's avatar Antti Palosaari Committed by Mauro Carvalho Chehab

[media] si2168: Implement own I2C adapter locking

We need own I2C locking because of tuner I2C adapter/repeater.
Firmware command is executed using I2C send + reply message. Default
I2C adapter locking protects only single I2C operation, not whole
send + reply sequence as needed. Due to that, it was possible tuner
I2C message interrupts firmware command sequence.
Reported-by: default avatarAdam Baker <linux@baker-net.org.uk>
Signed-off-by: default avatarAntti Palosaari <crope@iki.fi>
Reviewed-by: default avatarAdam Baker <linux@baker-net.org.uk>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent c1c3c85d
...@@ -18,23 +18,53 @@ ...@@ -18,23 +18,53 @@
static const struct dvb_frontend_ops si2168_ops; static const struct dvb_frontend_ops si2168_ops;
/* Own I2C adapter locking is needed because of I2C gate logic. */
static int si2168_i2c_master_send_unlocked(const struct i2c_client *client,
const char *buf, int count)
{
int ret;
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = count,
.buf = (char *)buf,
};
ret = __i2c_transfer(client->adapter, &msg, 1);
return (ret == 1) ? count : ret;
}
static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client,
char *buf, int count)
{
int ret;
struct i2c_msg msg = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = count,
.buf = buf,
};
ret = __i2c_transfer(client->adapter, &msg, 1);
return (ret == 1) ? count : ret;
}
/* execute firmware command */ /* execute firmware command */
static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) static int si2168_cmd_execute_unlocked(struct i2c_client *client,
struct si2168_cmd *cmd)
{ {
struct si2168_dev *dev = i2c_get_clientdata(client);
int ret; int ret;
unsigned long timeout; unsigned long timeout;
mutex_lock(&dev->i2c_mutex);
if (cmd->wlen) { if (cmd->wlen) {
/* write cmd and args for firmware */ /* write cmd and args for firmware */
ret = i2c_master_send(client, cmd->args, cmd->wlen); ret = si2168_i2c_master_send_unlocked(client, cmd->args,
cmd->wlen);
if (ret < 0) { if (ret < 0) {
goto err_mutex_unlock; goto err;
} else if (ret != cmd->wlen) { } else if (ret != cmd->wlen) {
ret = -EREMOTEIO; ret = -EREMOTEIO;
goto err_mutex_unlock; goto err;
} }
} }
...@@ -43,12 +73,13 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) ...@@ -43,12 +73,13 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
#define TIMEOUT 70 #define TIMEOUT 70
timeout = jiffies + msecs_to_jiffies(TIMEOUT); timeout = jiffies + msecs_to_jiffies(TIMEOUT);
while (!time_after(jiffies, timeout)) { while (!time_after(jiffies, timeout)) {
ret = i2c_master_recv(client, cmd->args, cmd->rlen); ret = si2168_i2c_master_recv_unlocked(client, cmd->args,
cmd->rlen);
if (ret < 0) { if (ret < 0) {
goto err_mutex_unlock; goto err;
} else if (ret != cmd->rlen) { } else if (ret != cmd->rlen) {
ret = -EREMOTEIO; ret = -EREMOTEIO;
goto err_mutex_unlock; goto err;
} }
/* firmware ready? */ /* firmware ready? */
...@@ -63,24 +94,32 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) ...@@ -63,24 +94,32 @@ static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
/* error bit set? */ /* error bit set? */
if ((cmd->args[0] >> 6) & 0x01) { if ((cmd->args[0] >> 6) & 0x01) {
ret = -EREMOTEIO; ret = -EREMOTEIO;
goto err_mutex_unlock; goto err;
} }
if (!((cmd->args[0] >> 7) & 0x01)) { if (!((cmd->args[0] >> 7) & 0x01)) {
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto err_mutex_unlock; goto err;
} }
} }
mutex_unlock(&dev->i2c_mutex);
return 0; return 0;
err:
err_mutex_unlock:
mutex_unlock(&dev->i2c_mutex);
dev_dbg(&client->dev, "failed=%d\n", ret); dev_dbg(&client->dev, "failed=%d\n", ret);
return ret; return ret;
} }
static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
{
int ret;
i2c_lock_adapter(client->adapter);
ret = si2168_cmd_execute_unlocked(client, cmd);
i2c_unlock_adapter(client->adapter);
return ret;
}
static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status)
{ {
struct i2c_client *client = fe->demodulator_priv; struct i2c_client *client = fe->demodulator_priv;
...@@ -569,60 +608,46 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, ...@@ -569,60 +608,46 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe,
/* /*
* I2C gate logic * I2C gate logic
* We must use unlocked i2c_transfer() here because I2C lock is already taken * We must use unlocked I2C I/O because I2C adapter lock is already taken
* by tuner driver. * by the caller (usually tuner driver).
*/ */
static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
{ {
struct i2c_client *client = mux_priv; struct i2c_client *client = mux_priv;
struct si2168_dev *dev = i2c_get_clientdata(client);
int ret; int ret;
struct i2c_msg gate_open_msg = { struct si2168_cmd cmd;
.addr = client->addr,
.flags = 0,
.len = 3,
.buf = "\xc0\x0d\x01",
};
mutex_lock(&dev->i2c_mutex);
/* open tuner I2C gate */ /* open I2C gate */
ret = __i2c_transfer(client->adapter, &gate_open_msg, 1); memcpy(cmd.args, "\xc0\x0d\x01", 3);
if (ret != 1) { cmd.wlen = 3;
dev_warn(&client->dev, "i2c write failed=%d\n", ret); cmd.rlen = 0;
if (ret >= 0) ret = si2168_cmd_execute_unlocked(client, &cmd);
ret = -EREMOTEIO; if (ret)
} else { goto err;
ret = 0;
}
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret; return ret;
} }
static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
{ {
struct i2c_client *client = mux_priv; struct i2c_client *client = mux_priv;
struct si2168_dev *dev = i2c_get_clientdata(client);
int ret; int ret;
struct i2c_msg gate_close_msg = { struct si2168_cmd cmd;
.addr = client->addr,
.flags = 0,
.len = 3,
.buf = "\xc0\x0d\x00",
};
/* close tuner I2C gate */
ret = __i2c_transfer(client->adapter, &gate_close_msg, 1);
if (ret != 1) {
dev_warn(&client->dev, "i2c write failed=%d\n", ret);
if (ret >= 0)
ret = -EREMOTEIO;
} else {
ret = 0;
}
mutex_unlock(&dev->i2c_mutex); /* close I2C gate */
memcpy(cmd.args, "\xc0\x0d\x00", 3);
cmd.wlen = 3;
cmd.rlen = 0;
ret = si2168_cmd_execute_unlocked(client, &cmd);
if (ret)
goto err;
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret; return ret;
} }
...@@ -679,8 +704,6 @@ static int si2168_probe(struct i2c_client *client, ...@@ -679,8 +704,6 @@ static int si2168_probe(struct i2c_client *client,
goto err; goto err;
} }
mutex_init(&dev->i2c_mutex);
/* create mux i2c adapter for tuner */ /* create mux i2c adapter for tuner */
dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
client, 0, 0, 0, si2168_select, si2168_deselect); client, 0, 0, 0, si2168_select, si2168_deselect);
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
/* state struct */ /* state struct */
struct si2168_dev { struct si2168_dev {
struct i2c_adapter *adapter; struct i2c_adapter *adapter;
struct mutex i2c_mutex;
struct dvb_frontend fe; struct dvb_frontend fe;
fe_delivery_system_t delivery_system; fe_delivery_system_t delivery_system;
fe_status_t fe_status; fe_status_t fe_status;
......
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