Skip to content

Commit

Permalink
[PATCH] i2c: i2c-mv64xxx fix transaction abortion
Browse files Browse the repository at this point in the history
When the i2c-mv64xxx i2c driver is signalled to abort a transaction,
it aborts it immediately by issuing a stop condition on the bus.
This violates the i2c protocol and can cause what appears to be an i2c
bus hang.  This patch delays issuing the stop condition until the i2c
device can reasonably expect a stop condition.

Also includes a minor fixup.

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
  • Loading branch information
Mark A. Greer authored and Greg Kroah-Hartman committed Jan 6, 2006
1 parent 7c72ccf commit e91c021
Showing 1 changed file with 15 additions and 18 deletions.
33 changes: 15 additions & 18 deletions drivers/i2c/busses/i2c-mv64xxx.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/*
* drivers/i2c/busses/i2c-mv64xxx.c
*
* Driver for the i2c controller on the Marvell line of host bridges for MIPS
* and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
*
Expand Down Expand Up @@ -65,7 +63,6 @@ enum {
MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
MV64XXX_I2C_STATE_ABORTING,
};

/* Driver actions */
Expand All @@ -85,6 +82,7 @@ struct mv64xxx_i2c_data {
int irq;
u32 state;
u32 action;
u32 aborting;
u32 cntl_bits;
void __iomem *reg_base;
u32 reg_base_p;
Expand Down Expand Up @@ -122,12 +120,6 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
return;
}

if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
return;
}

/* The status from the ctlr [mostly] tells us what to do next */
switch (status) {
/* Start condition interrupt */
Expand All @@ -148,14 +140,16 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
/* FALLTHRU */
case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
if (drv_data->bytes_left > 0) {
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;
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
drv_data->state =
MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
drv_data->bytes_left--;
} else {
drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
drv_data->state = MV64XXX_I2C_STATE_IDLE;
}
break;

Expand Down Expand Up @@ -184,7 +178,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
}
drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;

if (drv_data->bytes_left == 1)
if ((drv_data->bytes_left == 1) || drv_data->aborting)
drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
break;

Expand Down Expand Up @@ -320,6 +314,7 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
drv_data->msg = msg;
drv_data->byte_posn = 0;
drv_data->bytes_left = msg->len;
drv_data->aborting = 0;
drv_data->rc = 0;
drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
Expand Down Expand Up @@ -359,17 +354,19 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
}

if (abort && drv_data->block) {
drv_data->state = MV64XXX_I2C_STATE_ABORTING;
drv_data->aborting = 1;
spin_unlock_irqrestore(&drv_data->lock, flags);

time_left = wait_event_timeout(drv_data->waitq,
!drv_data->block,
msecs_to_jiffies(drv_data->adapter.timeout));

if (time_left <= 0) {
if ((time_left <= 0) && drv_data->block) {
drv_data->state = MV64XXX_I2C_STATE_IDLE;
dev_err(&drv_data->adapter.dev,
"mv64xxx: I2C bus locked\n");
"mv64xxx: I2C bus locked, block: %d, "
"time_left: %d\n", drv_data->block,
(int)time_left);
}
} else
spin_unlock_irqrestore(&drv_data->lock, flags);
Expand Down Expand Up @@ -510,7 +507,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
goto exit_kfree;
}

strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
I2C_NAME_SIZE);

init_waitqueue_head(&drv_data->waitq);
Expand Down

0 comments on commit e91c021

Please sign in to comment.