i2c: mv64xxx: separated transaction segments by repeated STARTs.

All transaction segments (all i2c_msg's passed to a single call to master_xfer)
should be separated on the bus by repeated START conditions. This change makes
that so with the exception of zero-byte reads (which don't make sense since the
slave will assume control of the data line). Zero-byte reads early terminate the
transaction.

Change-Id: I5ed9cbd94b6df0dc0bd33a7b8c0078e396fecd24
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index bbab0e1..e993488 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -75,6 +75,8 @@
 	MV64XXX_I2C_ACTION_SEND_DATA,
 	MV64XXX_I2C_ACTION_RCV_DATA,
 	MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+	MV64XXX_I2C_ACTION_RESTART,
+	MV64XXX_I2C_ACTION_RCV_DATA_RESTART,
 	MV64XXX_I2C_ACTION_SEND_STOP,
 };
 
@@ -98,6 +100,7 @@
 	wait_queue_head_t	waitq;
 	spinlock_t		lock;
 	struct i2c_msg		*msg;
+	int			msgs_left;	/* not counting msg */
 	struct i2c_adapter	adapter;
 };
 
@@ -156,11 +159,16 @@
 		/* 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->msgs_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 if ((drv_data->bytes_left == 0)
+				&& (drv_data->msgs_left > 0) ) {
+			drv_data->action = MV64XXX_I2C_ACTION_RESTART;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
 		} else {
 			drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
 			drv_data->state =
@@ -199,8 +207,14 @@
 		break;
 
 	case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
-		drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
-		drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		if (drv_data->msgs_left == 0) {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+			drv_data->state = MV64XXX_I2C_STATE_IDLE;
+		} else {
+			drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_RESTART;
+			drv_data->state =
+				MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+		}
 		break;
 
 	case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
@@ -224,6 +238,9 @@
 	}
 }
 
+static void mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+					struct i2c_msg *msg);
+
 static void
 mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
 {
@@ -276,6 +293,17 @@
 		wake_up_interruptible(&drv_data->waitq);
 		break;
 
+	case MV64XXX_I2C_ACTION_RCV_DATA_RESTART:
+		drv_data->msg->buf[drv_data->byte_posn++] =
+			readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+		/* FALLTHRU */
+	case MV64XXX_I2C_ACTION_RESTART:
+		writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+			drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+		drv_data->msgs_left--;
+		mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msg + 1);
+		break;
+
 	case MV64XXX_I2C_ACTION_INVALID:
 	default:
 		dev_err(&drv_data->adapter.dev,
@@ -389,8 +417,10 @@
 }
 
 static int
-mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+mv64xxx_i2c_execute_msgs(struct mv64xxx_i2c_data *drv_data,
+				struct i2c_msg *msgs, int nr_msgs)
 {
+	struct i2c_msg *msg = &msgs[0];
 	unsigned long	flags;
 
 	spin_lock_irqsave(&drv_data->lock, flags);
@@ -414,6 +444,7 @@
 	}
 
 	drv_data->block = 1;
+	drv_data->msgs_left = nr_msgs - 1;
 	mv64xxx_i2c_do_action(drv_data);
 	spin_unlock_irqrestore(&drv_data->lock, flags);
 
@@ -438,13 +469,13 @@
 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;
+	int	rc;
 
-	for (i=0; i<num; i++)
-		if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) < 0)
-			return rc;
+	rc = mv64xxx_i2c_execute_msgs(drv_data, msgs, num);
+	if (rc < 0)
+		return rc;
 
-	return num;
+	return num - drv_data->msgs_left;
 }
 
 static const struct i2c_algorithm mv64xxx_i2c_algo = {