]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/i2c/busses/i2c-mv64xxx.c
Merge branch 'master' into tk71
[mv-sheeva.git] / drivers / i2c / busses / i2c-mv64xxx.c
index 16242063144fa5a82d61e5c3d4f105f46963b783..a9941c65f226758d2992bb123fd0b5b2ead759dd 100644 (file)
@@ -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;
 }