mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 02:19:51 +00:00
i2c-mv64xxx: send repeated START between messages in xfer
As stated into file include/linux/i2c.h we must send a repeated START between messages in the same xfer groupset: * Except when I2C "protocol mangling" is used, all I2C adapters implement * the standard rules for I2C transactions. Each transaction begins with a * START. That is followed by the slave address, and a bit encoding read * versus write. Then follow all the data bytes, possibly including a byte * with SMBus PEC. The transfer terminates with a NAK, or when all those * bytes have been transferred and ACKed. If this is the last message in a * group, it is followed by a STOP. Otherwise it is followed by the next * @i2c_msg transaction segment, beginning with a (repeated) START. Signed-off-by: Rodolfo Giometti <giometti@linux.it> Signed-off-by: Mauro Barella <mbarella@vds-it.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
This commit is contained in:
parent
03ed6a3aa6
commit
eda6bee6c7
1 changed files with 38 additions and 7 deletions
|
@ -59,6 +59,7 @@ enum {
|
|||
MV64XXX_I2C_STATE_INVALID,
|
||||
MV64XXX_I2C_STATE_IDLE,
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_RESTART,
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
|
||||
|
@ -70,6 +71,7 @@ enum {
|
|||
MV64XXX_I2C_ACTION_INVALID,
|
||||
MV64XXX_I2C_ACTION_CONTINUE,
|
||||
MV64XXX_I2C_ACTION_SEND_START,
|
||||
MV64XXX_I2C_ACTION_SEND_RESTART,
|
||||
MV64XXX_I2C_ACTION_SEND_ADDR_1,
|
||||
MV64XXX_I2C_ACTION_SEND_ADDR_2,
|
||||
MV64XXX_I2C_ACTION_SEND_DATA,
|
||||
|
@ -91,6 +93,7 @@ struct mv64xxx_i2c_data {
|
|||
u32 addr2;
|
||||
u32 bytes_left;
|
||||
u32 byte_posn;
|
||||
u32 send_stop;
|
||||
u32 block;
|
||||
int rc;
|
||||
u32 freq_m;
|
||||
|
@ -159,8 +162,15 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
|
|||
if ((drv_data->bytes_left == 0)
|
||||
|| (drv_data->aborting
|
||||
&& (drv_data->byte_posn != 0))) {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
|
||||
drv_data->state = MV64XXX_I2C_STATE_IDLE;
|
||||
if (drv_data->send_stop) {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
|
||||
drv_data->state = MV64XXX_I2C_STATE_IDLE;
|
||||
} else {
|
||||
drv_data->action =
|
||||
MV64XXX_I2C_ACTION_SEND_RESTART;
|
||||
drv_data->state =
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_RESTART;
|
||||
}
|
||||
} else {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
|
||||
drv_data->state =
|
||||
|
@ -228,6 +238,15 @@ static void
|
|||
mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
|
||||
{
|
||||
switch(drv_data->action) {
|
||||
case MV64XXX_I2C_ACTION_SEND_RESTART:
|
||||
drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START;
|
||||
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
|
||||
writel(drv_data->cntl_bits,
|
||||
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
|
||||
drv_data->block = 0;
|
||||
wake_up_interruptible(&drv_data->waitq);
|
||||
break;
|
||||
|
||||
case MV64XXX_I2C_ACTION_CONTINUE:
|
||||
writel(drv_data->cntl_bits,
|
||||
drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
|
||||
|
@ -386,7 +405,8 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
|
|||
}
|
||||
|
||||
static int
|
||||
mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
|
||||
mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg,
|
||||
int is_first, int is_last)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -406,10 +426,18 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
|
|||
drv_data->bytes_left--;
|
||||
}
|
||||
} else {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
|
||||
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
|
||||
if (is_first) {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
|
||||
drv_data->state =
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
|
||||
} else {
|
||||
drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
|
||||
drv_data->state =
|
||||
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
|
||||
}
|
||||
}
|
||||
|
||||
drv_data->send_stop = is_last;
|
||||
drv_data->block = 1;
|
||||
mv64xxx_i2c_do_action(drv_data);
|
||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||
|
@ -437,9 +465,12 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
|
||||
int i, rc;
|
||||
|
||||
for (i=0; i<num; i++)
|
||||
if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) < 0)
|
||||
for (i = 0; i < num; i++) {
|
||||
rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i],
|
||||
i == 0, i + 1 == num);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue