Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eda6bee6 authored by Rodolfo Giometti's avatar Rodolfo Giometti Committed by Ben Dooks
Browse files

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: default avatarRodolfo Giometti <giometti@linux.it>
Signed-off-by: default avatarMauro Barella <mbarella@vds-it.com>
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
parent 03ed6a3a
Loading
Loading
Loading
Loading
+38 −7
Original line number Diff line number Diff line
@@ -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))) {
			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 {
		if (is_first) {
			drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
		drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
			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;
}